Adapter
Objective
It allows you to adapt or modify an existing interface in such a way that to otherwise incompatible classes can interact.
Function
Adapt an interface so that it can be used by a class that otherwise I couldn't use it.
Structure
This implementation uses the principle of object composition: the adapter implements the interface of one object and wraps around the other. It is can be implemented in all popular programming languages.
The structure that meets this pattern is shown in Figure 1
Figure 1: UML Diagram Adapter Pattern
Applications
- It is required to create a reusable class, which cooperates with unrelated classes or not intended; that is, classes that do not necessarily have compatible interfaces.
- Different sets of methods need to be supported for different purposes.
- You need to use an existing class via an interface that is incompatible with that class.
- You want to allow the user to select between different GUIs (Graphical User Interfaces), to exploit the same system in the way that is most convenient for him user comfort.
Design Patterns Collaborators
- No design patron is a collaborator.
Scope of action
Applied at the object level.
Problem
To make use of an object, whose interface is incompatible it is necessary to create a new one or otherwise modify the application so that can be integrated into that interface; in either case, the increase in complexity is remarkable.
Solution
The Adapter design pattern allows adapting the original interface of an object with one that is compatible with the object expected by the client class; that is, the pattern is seen as a thin layer of code between two objects called "syntactically incompatible".
Diagram or Implementation
Figure 2: UML Diagram Adapter Pattern
The behaviour of the adapter pattern is explained below using the sequence diagram in figure 2
- The Client class invokes the Adapter class with generic parameters.
- Adapter class converts generic parameters into specific parameters of the Adaptee component.
- The Adapter class invokes Adaptee.
- Adaptee class responds.
- The Adapter class converts the Adaptee response to a generic response for the client class.
- The Adapter class responds to Client with a generic response.
Study Cases
Banking Portal System
Figure 3: UML Diagram Banking Portal System
Figure 4: UML Diagram Banking Portal System
Reservation and Sales System
Figure 5: UML Diagram Reservation and Sales System
Figure 6: UML Diagram Reservation and Sales System
Implementations of the adapter pattern:
"""
Convert the interface of a class into another interface clients expect.
Adapter lets classes work together that couldn't otherwise because of
incompatible interfaces.
"""
import abc
class Target(metaclass=abc.ABCMeta):
"""
Define the domain-specific interface that Client uses.
"""
def __init__(self):
self._adaptee = Adaptee()
@abc.abstractmethod
def request(self):
pass
class Adapter(Target):
"""
Adapt the interface of Adaptee to the Target interface.
"""
def request(self):
self._adaptee.specific_request()
class Adaptee:
"""
Define an existing interface that needs adapting.
"""
def specific_request(self):
pass
def main():
adapter = Adapter()
adapter.request()
if __name__ == "__main__":
main()
/**
* The Target defines the domain-specific interface used by the client code.
*/
class Target
{
public function request(): string
{
return "Target: The default target's behavior.";
}
}
/**
* The Adaptee contains some useful behavior, but its interface is incompatible
* with the existing client code. The Adaptee needs some adaptation before the
* client code can use it.
*/
class Adaptee
{
public function specificRequest(): string
{
return ".eetpadA eht fo roivaheb laicepS";
}
}
/**
* The Adapter makes the Adaptee's interface compatible with the Target's
* interface.
*/
class Adapter extends Target
{
private $adaptee;
public function __construct(Adaptee $adaptee)
{
$this->adaptee = $adaptee;
}
public function request(): string
{
return "Adapter: (TRANSLATED) " . strrev($this->adaptee->specificRequest());
}
}
/**
* The client code supports all classes that follow the Target interface.
*/
function clientCode(Target $target)
{
echo $target->request();
}
echo "Client: I can work just fine with the Target objects:\n";
$target = new Target;
clientCode($target);
echo "\n\n";
$adaptee = new Adaptee;
echo "Client: The Adaptee class has a weird interface. See, I don't understand it:\n";
echo "Adaptee: " . $adaptee->specificRequest();
echo "\n\n";
echo "Client: But I can work with it via the Adapter:\n";
$adapter = new Adapter($adaptee);
clientCode($adapter);
/* The OLD */
class SquarePeg {
private double width;
public SquarePeg(double width) {
this.width = width;
}
public double getWidth() {
return width;
}
public void setWidth(double width) {
this.width = width;
}
}
/* The NEW */
class RoundHole {
private final int radius;
public RoundHole(int radius) {
this.radius = radius;
System.out.println("RoundHole: max SquarePeg is " + radius * Math.sqrt(2));
}
public int getRadius() {
return radius;
}
}
// Design a "wrapper" class that can "impedance match" the old to the new
class SquarePegAdapter {
// The adapter/wrapper class "has a" instance of the legacy class
private final SquarePeg squarePeg;
public SquarePegAdapter(double w) {
squarePeg = new SquarePeg(w);
}
// Identify the desired interface
public void makeFit(RoundHole roundHole) {
// The adapter/wrapper class delegates to the legacy object
double amount = squarePeg.getWidth() - roundHole.getRadius() * Math.sqrt(2);
System.out.println( "reducing SquarePeg " + squarePeg.getWidth() + " by " + ((amount < 0) ? 0 : amount) + " amount");
if (amount > 0) {
squarePeg.setWidth(squarePeg.getWidth() - amount);
System.out.println(" width is now " + squarePeg.getWidth());
}
}
}
public class AdapterDemoSquarePeg {
public static void main( String[] args ) {
RoundHole roundHole = new RoundHole( 5 );
SquarePegAdapter squarePegAdapter;
for (int i = 6; i < 10; i++) {
squarePegAdapter = new SquarePegAdapter((double)i);
// The client uses (is coupled to) the new interface
squarePegAdapter.makeFit(roundHole);
}
}
}