120 likes | 258 Views
Structural Patterns. Chapter 4 – Page 55. Structural Pattern: Adapter. Chapter 4 – Page 56. When the client expects to interact with a class in a particular manner, it is sometimes necessary to “adapt” the interface of the class to accommodate those expectations.
E N D
Structural Patterns Chapter 4 – Page 55
Structural Pattern: Adapter Chapter 4 – Page 56 When the client expects to interact with a class in a particular manner, it is sometimes necessary to “adapt” the interface of the class to accommodate those expectations. The client will call the adapter’s interface which, in turn, translates those calls into calls to the original interface of the class being adapted. This permits classes to work together in spite of incompatible interfaces.
The Adapter Pattern Chapter 4 – Page 57 The Client has been set up to collaborate with objects that conform to the domain-specific Target interface. The existing Adaptee interface is adapted to the Target interface by means of the Adapter. This solution is frequently needed when an old software component offers useful functionality, but its interface is incompatible with the architecture of a new system that is being developed.
Non-Software Example Chapter 4 – Page 58 The RoundHole client has been set up to check whether a RoundPeg target fits by checking its radius. The SquarePeg adaptee has a radius, not a width, but can be adapted to determine its “radius” by dividing its width by the square root of two. Note that the adaptee isn’t necessarily converted into an object of the target class, but it is adapted so the client’s needed functionality is available.
Primitive Software Example Chapter 4 – Page 59 The client uses the (x,y) position of a Rectangle’s upper left corner and the Rectangle’s width and height to draw the Rectangle. The old LegacyRectangle uses the (x,y) positions of its upper left and lower right corners to draw it. The RectangleAdapter adapts the old-style drawing to the new style by converting the client’s information about the one corner, the width, and the height into the LegacyRectangle’s information about the two corners.
C++ Code for Rectangle Adapter Chapter 4 – Page 60 #include <iostream> using namespace std; typedefintCoordinate; typedefintDimension; // Target Interface: Abstract Superclass class Rectangle { public: virtual void draw() = 0; }; // Adaptee: Old-style rectangle classLegacyRectangle { public: LegacyRectangle(Coordinate x1, Coordinate y1, Coordinate x2, Coordinate y2) { lowX = x1; lowY = y1; highX = x2; highY = y2; cout << "LegacyRectangle: create. (" << lowX << "," << lowY << ") => (" << highX << "," << highY << ")" << endl; } voidoldDraw() { cout << "LegacyRectangle: oldDraw. (" << lowX << "," << lowY << ") => (" << highX << "," << highY << ")" << endl; }
private: Coordinate lowX; Coordinate lowY; Coordinate highX; Coordinate highY; }; // Adapter: Rectangle is the abstract class providing the interface, while // LegacyRectangle is the concrete class providing the implementation. classRectangleAdapter: public Rectangle, privateLegacyRectangle { public: RectangleAdapter(Coordinate x, Coordinate y, Dimension w, Dimension h): LegacyRectangle(x, y, x + w, y + h) { cout << "RectangleAdapter: create. (" << x << "," << y << "), width = " << w << ", height = " << h << endl; } virtual void draw() { cout << "RectangleAdapter: draw." << endl; oldDraw(); } }; void main() { Rectangle *r = newRectangleAdapter(120, 200, 60, 40); r->draw(); } Chapter 4 – Page 61
Adapter Example: External Polymorphism Chapter 4 – Page 62 Polymorphism may be implemented even if the classes are not derived from the same base class. The ExecuteAdapter adapts the three old-style classes (Moe, Larry, and Curly) to the new target style (ExecuteInterface) by converting mapping each of their respective methods (doThis, doThat, and doTheOther) to the target’s execute method.
External Polymorphism C++ Code Chapter 4 – Page 63 #include <iostream> using namespace std; // Target: Specifies the new interface classExecuteInterface { public: virtual ~ExecuteInterface() {} virtual void execute() = 0; }; // Adapter: "Maps" the new interface to the legacy implementation template <class TYPE> classExecuteAdapter: publicExecuteInterface { public: ExecuteAdapter(TYPE *o, void(TYPE:: *m)()) { object = o; method = m; } ~ExecuteAdapter() { delete object; } void execute() { (object->*method)(); } private: TYPE *object; // ptr-to-object attribute void(TYPE:: *method)(); // ptr-to-member function attribute };
Chapter 4 – Page 64 // Adaptees: Three totally incompatible classes with // no common base class and no hope of polymorphism class Moe { public: ~Moe() { cout << "Moe::destructor" << endl; } voiddoThis() { cout << "Moe::doThis()" << endl; } }; class Larry { public: ~Larry() { cout << "Larry::destructor" << endl; } voiddoThat() { cout << "Larry::doThat()" << endl; } }; class Curly { public: ~Curly() { cout << "Curly::destructor" << endl; } voiddoTheOther() { cout << "Curly::doTheOther()" << endl; } };
// An array of new interfaces is returned, adapted from the old implementations ExecuteInterface** initialize() { ExecuteInterface** array = newExecuteInterface*[3]; array[0] = newExecuteAdapter<Moe> (new Moe(), &Moe::doThis); array[1] = newExecuteAdapter<Larry> (new Larry(), &Larry::doThat); array[2] = newExecuteAdapter<Curly> (new Curly(), &Curly::doTheOther); return array; } // The client uses the new interface, effectively producing an "external" polymorphism void main() { inti; ExecuteInterface** objects = initialize(); for (i = 0; i < 3; i++) objects[i]->execute(); for (i = 0; i < 3; i++) delete objects[i]; delete objects; } Chapter 4 – Page 65
Adapter Pattern Advantages Chapter 4 – Page 66 • The Adapter Design tends to make things work after they’ve already been designed, compelling normally incompatible designs to collaborate successfully. • By adapting new interfaces to old implementations, the old code becomes reusable. • In addition, the client is freed from the burden of having to account for object differences, since it treats all objects the same way. In addition, new types of objects can be accommodated without making changes to the client code.