Design Patterns - Adapter
The adapter pattern is a software design pattern that allows the interface of an existing class to be used as another interface.
The adapter pattern is a software design pattern that allows the interface of an existing class to be used as another interface. It is often used to make existing classes work with others without modifying their source code.
Converts one interface to another so that it matches what the client is expecting.
Convert the interface of a class into another interface clients expect. Adapter lets classes work together that couldn't otherwise because of incompatible interfaces.
Sometimes a toolkit class that's designed for reuse isn't reusable only because its interface doesn't match the domain-specific interface an application requires.
We could define TextShape so that it adapts the TextView interface to Shape's.
We can do this in one of two ways:
- by inheriting Shape's interface and TextView's implementation or
- by composing a TextView instance within a TextShape and implementing TextShape in terms of TextView's interface.
These two approaches correspond to the class and object versions of the Adapter pattern.
We call TextShape an adapter.
Use the Adapter pattern when:
- You want to use an existing class, and its interface does not match the one you need.
- You want to create a reusable class that cooperates with unrelated or unforeseen classes, that is, classes that don't necessarily have compatible interfaces.
- You need to use several existing subclasses, but it's impractical to adapt their interface by subclassing every one. An object adapter can adapt the interface of its parent class.
A class adapter uses multiple inheritance to adopt one interface to another. An object adapter relies on object composition:
Target - defines the domain-specific interface that Client uses.
Client - collaborates with objects conforming to the Target interface.
Adaptee - defines an existing interface that needs adapting.
Adapter - adapts the interface of Adaptee to the Target interface.
Clients call operations on an Adapter instance. In turn, the adapter calls Adaptee operations that carry out the request.
Class and object adapters have different trade-offs. A class adapter :
- adapts Adaptee to Target by committing to a concrete Adapter class. As a consequence, a class adapter won't work when we want to adapt a class and all its subclasses.
- lets Adapter override some of Adaptee's behavior, since Adapter is a subclass of Adaptee.
- introduces only one object, and no additional pointer indirection is needed to get to the adaptee.
An object adapter:
- lets a single Adapter work with many Adaptees—that is, the Adaptee itself and all of its subclasses (if any). The Adapter can also add functionality to all Adaptees at once.
- makes it harder to override Adaptee behavior. It will require subclassing Adaptee and making Adapter refer to the subclass rather than the Adaptee itself.
Issues to consider:
- How much adapting does Adapter do? The amount of work Adapter does depends on how similar the Target interface is to Adaptee's.
- Pluggable adapters.
- A class is more reusable when you minimize the assumptions other classes must make to use it.
- By building interface adaptation into a class, you eliminate the assumption that other classes see the same interface.
- Put another way, interface adaptation lets us incorporate our class into existing systems that might expect different.
- Using two-way adapters to provide transparency
- Potential problem with adapters is that they aren't transparent to all clients.
- An adapted object no longer conforms to the Adaptee interface, so it can't be used as is wherever an Adaptee object can.
- Two-way adapters can provide such transparency. Specifically, they're useful when two different clients need to view an object differently.