Adapter Pattern
Motivation
The adapter pattern is adapting between classes and objects. Like any adapter in the real world it is used to be an interface, a bridge between two objects. In real world we have adapters for power supplies, adapters for camera memory cards, and so on. Probably everyone have seen some adapters for memory cards. If you can not plug in the camera memory in your laptop you can use and adapter. You plug the camera memory in the adapter and the adapter in to laptop slot. That's it, it's really simple.What about software development? It's the same. Can you imagine an situation when you have some class expecting some type of object and you have an object offering the same features, but exposing a different interface? Of course, you want to use both of them so you don't to implement again one of them, and you don't want to change existing classes, so why not create an adapter...
Intent
- Convert the interface of a class into another interface clients expect.
- Adapter lets classes work together, that could not otherwise because of incompatible interfaces.
Implementation
The figure below shows a UML class diagram for the Adapter Pattern:The classes/objects participating in adapter pattern:
- Target - defines the domain-specific interface that Client uses.
- Adapter - adapts the interface Adaptee to the Target interface.
- Adaptee - defines an existing interface that needs adapting.
- Client - collaborates with objects conforming to the Target interface.
Applicability & Examples
The visitor pattern is used when:- When you have a class(Target) that invokes methods defined in an interface and you have a another class(Adapter) that doesn't implement the interface but implements the operations that should be invoked from the first class through the interface. You can change none of the existing code. The adapter will implement the interface and will be the bridge between the 2 classes.
- When you write a class (Target) for a generic use relying on some general interfaces and you have some implemented classes, not implementing the interface, that needs to be invoked by the Target class.
- Non Software Examples of Adapter Patterns : Power Supply Adapters, card readers and adapters, ... Software Examples of Adapter Patterns: Wrappers used to adopt 3rd parties libraries and frameworks - most of the applications using third party libraries use adapters as a middle layer between the application and the 3rd party library to decouple the application from the library. If another library has to be used only an adapter for the new library is required without having to change the application code.
Specific problems and implementation
Objects Adapters - Based on Delegation
Objects Adapters are the classical example of the adapter pattern. It uses composition, the Adaptee delegates the calls to Adaptee (opossed to class adapters which extends the Adaptee). This behaviour gives us a few advantages over the class adapters(however the class adapters can be implemented in languages allowing multiple inheritance). The main advantage is that the Adapter adapts not only the Adpatee but all its subclasses. All it's subclasses with one "small" restriction: all the subclasses which don't add new methods, because the used mechanism is delegation. So for any new method the Adapter must be changed or extended to expose the new methods as well. The main disadvantage is that it requires to write all the code for delegating all the necessary requests tot the Adaptee.UML Diagram
Class adapters can be implemented in languages supporting multiple inheritance(Java, C# or PHP does not support multiple inheritance). Thus, such adapters can not be easy implemented in Java, C# or VB.NET. Class adapter uses inheritance instead of composition. It means that instead of delegating the calls to the Adaptee, it subclasses it. In conclusion it must subclass both the Target and the Adaptee. There are advantages and disadvantages:
- It adapts the specific Adaptee class. The class it extends. If that one is subclassed it can not be adapted by the existing adapter.
- It doesn't require all the code required for delegation, which must be written for an Object Adapter.
How Much the Adapter Should Do?
This question has a really simple response: it should do how much it has to in order to adapt. It's very simple, if the Target and Adaptee are similar then the adapter has just to delegate the requests from the Target to the Adaptee. If Target and Adaptee are not similar, then the adapter might have to convert the data structures between those and to implement the operations required by the Target but not implemented by the Adaptee.Two-Ways Adapters
The Two-Ways Adapters are adapters that implements both interfaces of Target and Adaptee. The adapted object can be used as Target in new systems dealing with Target classes or as Adaptee in other systems dealing with Adaptee classes. Going further on this line of thinking, we can have adapters implementing n interfaces, adapting to n systems. Two-way adapters and n-way adapters are hard to implement in systems not supporting multiple inheritance. If adapter has to extend the Target class it can not extent another class like Adaptee, so the Adaptee should be an interface and all the calls should be delegated from the adapter to the Adaptee object.Adapter Pattern and Strategy Pattern
Adapter Pattern and Strategy Pattern - there are many cases when the adapter can play the role of the Strategy Pattern. If we have several modules implementing the same functionality and we wrote adapters for them, the adapters are implementing the same interface. We can simply replace the adapters objects at run time because they implements the same interface.
* Example of `adapter' design pattern
* Copyright (C) 2011 Radek Pazdera
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include#includetypedef int Cable; // wire with electrons :-)/* Adaptee (source) interface */class EuropeanSocketInterface{public:virtual int voltage() = 0;virtual Cable live() = 0;virtual Cable neutral() = 0;virtual Cable earth() = 0;};/* Adaptee */class Socket : public EuropeanSocketInterface{public:int voltage() { return 230; }Cable live() { return 1; }Cable neutral() { return -1; }Cable earth() { return 0; }};/* Target interface */class USASocketInterface{public:virtual int voltage() = 0;virtual Cable live() = 0;virtual Cable neutral() = 0;};/* The Adapter */class Adapter : public USASocketInterface{EuropeanSocketInterface* socket;public:void plugIn(EuropeanSocketInterface* outlet){socket = outlet;}int voltage() { return 110; }Cable live() { return socket->live(); }Cable neutral() { return socket->neutral(); }};/* Client */class ElectricKettle{USASocketInterface* power;public:void plugIn(USASocketInterface* supply){power = supply;}void boil(){if (power->voltage() > 110){std::cout << "Kettle is on fire!" << std::endl;return;}if (power->live() == 1 && power->neutral() == -1){std::cout << "Coffee time!" << std::endl;}}};int main(){Socket* socket = new Socket;Adapter* adapter = new Adapter;ElectricKettle* kettle = new ElectricKettle;/* Pluging in. */adapter->plugIn(socket);kettle->plugIn(adapter);/* Having coffee */kettle->boil();return 0;}
No comments:
Post a Comment