This article is lecture note from "Head First Design Patterns" by Elisabeth Freeman and Kathy Sierra and CS course from Kookmin by Inkyu Kim
The Adapter Pattern converts the interface of a class into another interface the client expect. Adapter lets classes work together that couldn't otherwise because of its incompatible interfaces.
Adapter wraps existing class with a new interface. This acts to decouple the client from the implemented interface and if we expect the interface change over time, the adapter encapsulates the change so that the client doesn't have to be modified each time it need to operate against a different interface.
In the real world we can 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 work with 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.
The Adapter Pattern is full of good OO design principle: check out the use of object composition to wrap the adaptee with an altered interface. Also check out how the pattern binds the client to an interface not an implementation.
First of all, we can see the Client is a class that contains the existing buisness logic of the program. The Client Interface describes a protocol that other classes must follow to be able to collaborate with the client code.
The Service is some useful class (usually 3rd-party of legacy). The client can't use this class directly because it has an incompatible interface.
The Adapter is a class that's able to work with both the client and the service. It implements the client interface, while wrapping the service object(it is really impressive to me). The adapter receives calls from the client via the adapter interface and translates them into calls to the wrapped service object in a format it can understand.
Finally, the client code doesn't get coupled to the concrete adapter class as long as it works with the adapter via the clent interface. Thanks to this, you can introduce new types of adapters into the program without breaking the existing client code.(totally satisfy OCP) This can be useful when the interface of the service class gets changed or replaced. You can just create a new adapter class without changing the client code(even including existing services👍 ).
plus,
This implementation uses inheritance: the adapter inherits from both objects at the same time. Note that this approach can only be implemented in programming languages that support multiple inheritance, such as C++.
The Class Adapter does not need to wrap any objects because it inherits behaviours from both client and the service. The adaptation happens within the overridden methods. The resulting adapter can be used in place of an existing client class.
As I just said, from a point of view of Open Closed Principle, it is absolutely good to introduce new types of adapters into the program without breaking the existing client code, as long as they work with the adapters through the client interface. In addition, Single Responsibility Principle is satisfied because you can separate the interface or data conversion code from the primary buisiness logic of the program.
it's a bit long...
<Duck.java>
public interface Duck {
public void quack();
public void fly();
}
<MallardDuck.java>
public class MallardDuck implements Duck {
public void quack() {
System.out.println("Quack");
}
public void fly() {
System.out.println("I'm flying");
}
}
<Turkey.java>
public interface Turkey {
public void gobble();
public void fly();
}
<WildTurkey.java>
public class WildTurkey implements Turkey {
public void gobble() {
System.out.println("Gobble gobble");
}
public void fly() {
System.out.println("I'm flying a short distance");
}
}
<TurkeyAdapter.java>
public class TurkeyAdapter implements Duck {
Turkey turkey;
public TurkeyAdapter(Turkey turkey) {
this.turkey = turkey;
}
public void quack() {
turkey.gobble();
}
public void fly() {
for(int i=0; i < 5; i++) {
turkey.fly();
}
}
}
<DuckTestDrive.java>
public class DuckTestDrive {
public static void main(String[] args) {
Duck duck = new MallardDuck();
Turkey turkey = new WildTurkey();
Duck turkeyAdapter = new TurkeyAdapter(turkey);
System.out.println("The Turkey says...");
turkey.gobble();
turkey.fly();
System.out.println("\nThe Duck says...");
testDuck(duck);
System.out.println("\nThe TurkeyAdapter says...");
testDuck(turkeyAdapter);
}
static void testDuck(Duck duck) {
duck.quack();
duck.fly();
}
}
Our ducks implement a Duck interface that allows Ducks fly and quack. Tukey, by the way, doesn't quack, they gobble.
So in TurkeyAdapter we implement the interface of the type we're adapting to. The Duck is the interface we expect to see. And then we need to get a reference to the object that we are adapting; here we do thatthrough the construction.
public TurkeyAdapter(Turkey turkey) {
this.turkey = turkey;
}
Now we need to implement all the methods in the interface; the quack() translation between classes is eacy, just call the gobble method 😀.
Even though both interfaces have a fly() method, we need to map between Duck's fly() and Turkey's fly(). We need to call the Turkey's fly() method five times to make up for it (Turkey fly short time).
Adapter changes the interface of an existing object, while Decorator enhances an object without changing its interface. In addition, Decorator supports recursive composition, which isn't possible when you use Adapter.
Adapter provides a different interface to the wrapped object, Proxy provides it with the same interface, and Decorator provides it with an enhanced interface.
Facade defines a new interface for existing objects, whereas Adapter tries to make the existing interface usable. Adapter usually wraps just one object, while Facade works with an entire subsystem of objects.