Adapter Design Pattern (Structural)
Adapter is a structural design pattern that allows objects with incompatible interfaces to collaborate.
Problem
Section titled βProblemβImagine that youβre creating a stock market monitoring app. The app downloads the stock data from multiple sources in XML format and then displays nice-looking charts and diagrams for the user.
At some point, you decide to improve the app by integrating a smart 3rd-party analytics library. But thereβs a catch: the analytics library only works with data in JSON format.
You could change the library to support XML. However, this might break some existing code that relies on the library. And worse, you might not have access to the libraryβs source code in the first place, making this approach impossible.
Solution
Section titled βSolutionβYou can create an adapter. This is a special object that converts the interface of one object so that another object can understand it.
An adapter wraps one of the objects to hide the complexity of conversion happening behind the scenes. The wrapped object isnβt even aware of the adapter. For example, you can wrap an object that operates in meters and kilometers with an adapter that converts all of the data to imperial units such as feet and miles.
Adapters can not only convert data into various formats but can also help objects with different interfaces collaborate. Hereβs how it works:
- The adapter gets an interface, compatible with one of the existing objects.
- Using this interface, the existing object can safely call the adapterβs methods.
- Upon receiving a call, the adapter passes the request to the second object, but in a format and order that the second object expects.
Sometimes itβs even possible to create a two-way adapter that can convert the calls in both directions.
Real-World Analogy
Section titled βReal-World AnalogyβWhen you travel from Europe to the US for the first time, you might get a surprise when trying to charge your laptop. The power plug and sockets standards are different in different countries. Thatβs why your European plug wonβt fit a US socket. The problem can be solved by using a power plug adapter that has the American-style socket and the European-style plug.
Structure (Class Diagram)
Section titled βStructure (Class Diagram)βclassDiagram
class Client {
}
class ClientInterface {
<<interface>>
+method(data)
}
class Adapter {
-adaptee: Adaptee
+method(data)
}
class Adaptee {
+specificRequest(specialData)
}
Client --> ClientInterface
Adapter ..|> ClientInterface
Adapter --> Adaptee : wraps/delegates
Pseudocode
Section titled βPseudocodeβ# The Client Interface describes a protocol that other# classes must follow to be able to work with the client code.interface RoundPeg: method getRadius(): int
# A class that you want to make compatible with your code.class SquarePeg: field width: int constructor SquarePeg(width): this.width = width method getWidth(): int return this.width
# The Adapter allows using square pegs in round holes.class SquarePegAdapter implements RoundPeg: private peg: SquarePeg
constructor SquarePegAdapter(peg: SquarePeg): this.peg = peg
method getRadius(): int # The adapter pretends that it's a round peg with a # radius that can fit the square peg that the adapter # actually wraps. return this.peg.getWidth() * sqrt(2) / 2Applicability
Section titled βApplicabilityβ- Use the Adapter class when you want to use some existing class, but its interface isnβt compatible with the rest of your code.
- Use the pattern when you want to reuse several existing subclasses that lack some common functionality that canβt be added to the superclass.
How to Implement
Section titled βHow to Implementβ- Ensure that you have at least two classes with incompatible interfaces:
- A useful Service (usually 3rd-party or legacy) which you canβt change.
- A Client that would benefit from using the service class.
- Declare the client interface and describe how the client communicates with the service.
- Create the adapter class and make it follow the client interface. Leave all methods empty for now.
- Add a field to the adapter class to store a reference to the service object.
- One by one, implement all methods of the client interface in the adapter class. The adapter should delegate most of the real work to the service object, handling only the interface or data format conversion.
- Clients should use the adapter via the client interface. This will let you change or extend the adapters without affecting the client code.
Pros and Cons
Section titled βPros and ConsβPros:
- Single Responsibility Principle. You can separate the interface or data conversion code from the primary business logic of the program.
- Open/Closed Principle. You can introduce new types of adapters into the program without breaking the existing client code, as long as they work with the client interface.
Cons:
- The overall complexity of the code increases because you need to introduce a set of new interfaces and classes. Sometimes itβs simpler just to change the service class so that it fits the rest of your code.
Relations with other Design Patterns
Section titled βRelations with other Design Patternsβ- Adapter changes the interface of an existing object, while Decorator enhances an object without changing its interface.
- Adapter provides a different interface to its subject. Proxy provides it with the same interface. Decorator provides it with an enhanced interface.
- Bridge is usually designed up-front, letting you develop parts of an application independently of each other. Adapter is commonly used with an existing app to make some otherwise-incompatible classes work together nicely.
Code Examples
Section titled βCode ExamplesβCode Examples (C#)
Section titled βCode Examples (C#)β// Interface expected by the clientpublic interface ITarget{ void Request();}
// Existing class with incompatible interface.public class Adaptee{ public void SpecificRequest() { Console.WriteLine("Call for the specific Adaptee method."); }}
// Adapterpublic class Adapter : ITarget{ private readonly Adaptee _adaptee;
public Adapter(Adaptee adaptee) { _adaptee = adaptee; }
public void Request() { _adaptee.SpecificRequest(); }}
// Usageclass Program{ static void Main() { var adaptee = new Adaptee(); ITarget target = new Adapter(adaptee);
target.Request(); }}Code Examples (Python)
Section titled βCode Examples (Python)βclass Target: """ The Target defines the domain-specific interface used by the client code. """ def request(self) -> str: return "Target: The default target's behavior."
class Adaptee: """ The Adaptee contains some useful behavior, but its interface is incompatible with the existing client code. """ def specific_request(self) -> str: return ".eetpadA eht fo roivaheb laicepS"
class Adapter(Target): """ The Adapter makes the Adaptee's interface compatible with the Target's interface. """ def __init__(self, adaptee: Adaptee) -> None: self.adaptee = adaptee
def request(self) -> str: return f"Adapter: (TRANSLATED) {self.adaptee.specific_request()[::-1]}"
def client_code(target: Target) -> None: """ The client code supports all classes that follow the Target interface. """ print(target.request(), end="")
if __name__ == "__main__": print("Client: I can work just fine with the Target objects:") target = Target() client_code(target) print("\\n")
adaptee = Adaptee() print("Client: The Adaptee class has a weird interface. See, I don't understand it:") print(f"Adaptee: {adaptee.specific_request()}", end="\\n\\n")
print("Client: But I can work with it via the Adapter:") adapter = Adapter(adaptee) client_code(adapter)Code Examples (C++)
Section titled βCode Examples (C++)β#include <iostream>
// Interface expected by the clientclass ITarget {public: virtual ~ITarget() = default; virtual void Request() = 0;};
// Existing class with incompatible interfaceclass Adaptee {public: void SpecificRequest() { std::cout << "Call for the specific Adaptee method." << std::endl; }};
// Adapterclass Adapter : public ITarget {private: Adaptee& adaptee;
public: Adapter(Adaptee& a) : adaptee(a) {}
void Request() override { adaptee.SpecificRequest(); }};
// Usageint main() { Adaptee adaptee; ITarget* target = new Adapter(adaptee);
target->Request();
delete target; return 0;}Code Examples (Java)
Section titled βCode Examples (Java)β// Target Interfacepublic interface ITarget { void request();}
// Adaptee (Existing Incompatible Class)public class Adaptee { public void specificRequest() { System.out.println("Call for the specific Adaptee method."); }}
// Adapterpublic class Adapter implements ITarget {
private final Adaptee adaptee;
public Adapter(Adaptee adaptee) { this.adaptee = adaptee; }
@Override public void request() { adaptee.specificRequest(); }}
// Usagepublic class Program { public static void main(String[] args) { Adaptee adaptee = new Adaptee(); ITarget target = new Adapter(adaptee);
target.request(); }}Code Examples (JavaScript)
Section titled βCode Examples (JavaScript)β// Target Interface (simulated)class ITarget { request() { throw new Error("request() must be implemented"); }}
// Adaptee (existing incompatible class)class Adaptee { specificRequest() { console.log("Call for the specific Adaptee method."); }}
// Adapterclass Adapter extends ITarget { constructor(adaptee) { super(); this.adaptee = adaptee; }
request() { this.adaptee.specificRequest(); }}
// Usageconst adaptee = new Adaptee();const target = new Adapter(adaptee);
target.request();Code Examples (Visual Basic)
Section titled βCode Examples (Visual Basic)β' ============================' Interface expected by the client' ============================Public Interface ITarget Sub Request()End Interface
' ============================' Existing class with incompatible interface' ============================Public Class Adaptee Public Sub SpecificRequest() Console.WriteLine("Call for the specific Adaptee method.") End SubEnd Class
' ============================' Adapter' ============================Public Class Adapter Implements ITarget
Private ReadOnly _adaptee As Adaptee
Public Sub New(adaptee As Adaptee) _adaptee = adaptee End Sub
Public Sub Request() Implements ITarget.Request _adaptee.SpecificRequest() End SubEnd Class
' ============================' Usage' ============================Module Program Sub Main() Dim adaptee As New Adaptee() Dim target As ITarget = New Adapter(adaptee)
target.Request() End SubEnd ModuleCode Examples (Rust)
Section titled βCode Examples (Rust)β// Interface expected by the clienttrait Target { fn request(&self);}
// Existing class with incompatible interfacestruct Adaptee;
impl Adaptee { fn specific_request(&self) { println!("Call for the specific Adaptee method."); }}
// Adapterstruct Adapter { adaptee: Adaptee,}
impl Adapter { fn new(adaptee: Adaptee) -> Self { Self { adaptee } }}
impl Target for Adapter { fn request(&self) { self.adaptee.specific_request(); }}
// Usagefn main() { let adaptee = Adaptee; let target: Box<dyn Target> = Box::new(Adapter::new(adaptee));
target.request();}Code Examples (PHP)
Section titled βCode Examples (PHP)β<?php
// Interface expected by the clientinterface ITarget{ public function request(): void;}
// Existing class with incompatible interfaceclass Adaptee{ public function specificRequest(): void { echo "Call for the specific Adaptee method.\n"; }}
// Adapterclass Adapter implements ITarget{ private Adaptee $adaptee;
public function __construct(Adaptee $adaptee) { $this->adaptee = $adaptee; }
public function request(): void { $this->adaptee->specificRequest(); }}
// Usage$adaptee = new Adaptee();$target = new Adapter($adaptee);
$target->request();Code Examples (Go)
Section titled βCode Examples (Go)βpackage main
import ( "fmt")
// Interface expected by the clienttype Target interface { Request()}
// Existing class with incompatible interfacetype Adaptee struct{}
func (Adaptee) SpecificRequest() { fmt.Println("Call for the specific Adaptee method.")}
// Adaptertype Adapter struct { adaptee Adaptee}
func NewAdapter(a Adaptee) *Adapter { return &Adapter{adaptee: a}}
func (ad *Adapter) Request() { ad.adaptee.SpecificRequest()}
// Usagefunc main() { adaptee := Adaptee{} var target Target = NewAdapter(adaptee)
target.Request()}Code Examples (Ruby)
Section titled βCode Examples (Ruby)β# Interface expected by the clientclass Targetdef request raise NotImplementedError, "Subclasses must implement request"endend
# Existing class with incompatible interfaceclass Adapteedef specific_request puts "Call for the specific Adaptee method."endend
# Adapterclass Adapter < Targetdef initialize(adaptee) @adaptee = adapteeend
def request @adaptee.specific_requestendend
# Usageadaptee = Adaptee.newtarget = Adapter.new(adaptee)
target.requestCode Examples (Perl)
Section titled βCode Examples (Perl)β#!/usr/bin/perluse strict;use warnings;
############################# Target (Interface-like)############################package Target;
sub request { die "request() must be implemented by subclass";}
############################# Adaptee (Existing class)############################package Adaptee;
sub new { bless {}, shift }
sub specific_request { print "Call for the specific Adaptee method.\n";}
############################# Adapter############################package Adapter;our @ISA = ('Target');
sub new { my ($class, $adaptee) = @_; return bless { adaptee => $adaptee }, $class;}
sub request { my ($self) = @_; $self->{adaptee}->specific_request();}
############################# Usage############################package main;
my $adaptee = Adaptee->new();my $target = Adapter->new($adaptee);
$target->request();Code Examples (TypeScript)
Section titled βCode Examples (TypeScript)β// Target Interfaceinterface ITarget { request(): void;}
// Adaptee (existing incompatible class)class Adaptee { specificRequest(): void { console.log("Call for the specific Adaptee method."); }}
// Adapterclass Adapter implements ITarget { private adaptee: Adaptee;
constructor(adaptee: Adaptee) { this.adaptee = adaptee; }
request(): void { this.adaptee.specificRequest(); }}
// Usageconst adaptee = new Adaptee();const target: ITarget = new Adapter(adaptee);
target.request();