1.1. Intent ¶
ํด๋์ค์ ์ธํฐํ์ด์ค๋ฅผ ๋ค๋ฅธ ํ์ํ ํด๋์ค์ ์ธํฐํ์ด์ค์ ๋ง๊ฒ ๋ณํํด์ค๋ค. Adapter ๋ ์๋ก ํธํ์ฑ์ด ์๋ ์ธํฐํ์ด์ค๋ค๋ผ๋ฆฌ๋ผ๋ ๊ฐ์ด ์๋ํ ์ ์๊ฒ๋ ํด์ค๋ค.
1.4. Applicability ¶
๋ค์๊ณผ ๊ฐ์ ๊ฒฝ์ฐ์ AdapterPattern๋ฅผ ์ด์ฉํ๋ค.
- ์ด๋ฏธ ๋ง๋ค์ด์ ธ ์๋ ํด๋์ค๋ฅผ ์ฌ์ฉํ๊ณ ์ถ์ง๋ง, ์ธํฐํ์ด์ค๊ฐ ์ํ๋ ๋ฐฉ์๊ณผ ์ผ์นํ์ง ์์๋.
- ๊ด๋ จ์ฑ์ด ์๊ฑฐ๋, ์์ธกํ์ง ๋ชปํ ํด๋์ค๋ค๊ณผ ํ๋ํ๋ ์ฌ์ฌ์ฉ๊ฐ๋ฅํ ํด๋์ค๋ฅผ ์์ฑํ๊ธฐ ์ํ ๋. ์ด ๊ฒฝ์ฐ ํด๋์ค๋ค์ด ํธํ์ฑ์ ๊ฐ์ง๋ ์ธํฐํ์ด์ค๋ฅผ ํ์๋ก ํ์ง ์๋๋ค.
- (object adapter ์ ๊ฒฝ์ฐ์๋ง ํด๋น) ํ์ฌ ์ด๋ฏธ ๋ง๋ค์ด์ง ์ฌ๋ฌ๊ฐ์ subclass๊ฐ ํ์ํ ๊ฒฝ์ฐ, ํ์ง๋ง ๊ฐ๊ฐ์ ์๋ธํด๋์ค๋ค์ ๋ํ ์ธํฐํ์ด์ค๋ฅผ ํ๋ ๊ฒ์ ๋นํจ์จ์ ์ด๋ค. ์ด ๊ฒฝ์ฐ parent class์ ์ธํฐํ์ด์ค๋ฅผ adapt ํ ์ ์๋ค.
1.5. Structure ¶
adapter ํด๋์ค๋ ํ๋์ interface๋ฅผ ๋ค๋ฅธ interface ์ ์ ํฉํ๊ฒ ๋ง์ถฐ์ฃผ๊ธฐ ์ํด (๋ง ๊ทธ๋๋ก ์ด๋ํฐ ์ญํ ~) ๋ค์ค์์์ ์ด์ฉํ๋ค.
adapter ๊ฐ์ฒด๋ object composition ์ ์์กดํ๋ค.
1.6. Participants ¶
- Target (Shape)
- ํด๋ผ์ด์ธํธ๊ฐ ์ด์ฉํ domain-specificํ ์ธํฐํ์ด์ค๋ฅผ ์ ์ํ๋ค.
- Client (DrawingEditor)
- Target์ ์ธํฐํ์ด์ค์ ๋ฐ๋ผ ๊ฐ์ฒด๋ค๊ณผ ์ํธ์์ฉ์ ํ๋ค.
- Adaptee (TextView)
- adapting์ด ํ์ํ ํ์กดํ๋ interface๋ฅผ ์ ์ํ๋ค.
- Adapter (TextShape)
- Adpatee ์ ์ธํฐํ์ด์ค๋ฅผ Target ์ ์ธํฐํ์ด์ค์ adapt ์ํจ๋ค.
1.7. Collaborations ¶
- ํด๋น ํด๋์ค๋ฅผ ์ด์ฉํ๋ Client๋ค์ Adapter ์ธ์คํด์ค์ operation๋ค์ ํธ์ถํ๋ค. adapter๋ ํด๋น Client์ ์์ฒญ์ ์ํํ๊ธฐ ์ํด Adaptee ์ operation์ ํธ์ถํ๋ค.
1.9. Implementation ¶
~cpp directoryDisplay := (TreeDisplay on: treeRoot) getChildrenBlock: [:node | node getSubdirectories] createGraphicNodeBlock: [:node | node createGraphicNode].
1.10. Sample Code ¶
We'll give a brief sketch of the implementation of class and object adapters for the Motivation example beginning with the classes Shape and TextView.
~cpp class Shape { public: Shape (); virtual void BoundingBox(Point& bottomLeft, Point& topRight) const; virtual Manipulator* CreateManipulator () const; }; class TextView { public: TextView (); void GetOrigin (Coord& x, Coord& y) const; void GetExtent (Coord& width, Coord& height) const; virtual bool IsEmpty () const;
Shape assumes a bounding box defined by its opposing corners. In contrast, TextView is defined by an origin, height, and width. Shape also defines a CreateManipulator operation for creating a Manipulator object, which knowns how to animate a shape when the user manipulates it. TextView has no equivalent operation. The class TextShape is an adapter between these different interfaces.
A class adapter uses multiple inheritance to adapt interfaces. The key to class dapters is to use one inheritance branch to inherit the interface and another branch to inherit the implementation. The usual way to make this distinction in C++ is to inherit the interface publicly and inherit the implementation privately. We'll use this convention to define the TextShape adapter.
~cpp class TextShape : public Shape, private TextView { public: TextShape (); virtual void BoundingBox (Point& bottomLeft, Point& topRight) const; virtual bool IsEmpty () const; virtual Manipulator* CreateManipulator () const; };
The BoundingBox operation converts TextView's interface to conform to Shape's.
~cpp void TextShape::boundingBox (Point& bottomLeft, Point& topRight) const { Coord bottom, left, width, height; GetOrigin (bottom, left); GetExtent (width, height); bottomLeft = Point (bottom, left); topRight = Point (bottom + height, left + width);
The IsEmpty operations demonstrates the direct forwarding of requests common in adapter implementations:
~cpp bool TextShape::ImEmpty () const { return TextView::IsEmpty (); }
Finally, we define CreateManipulator (which isn't supported by TextView) from scratch. Assume we've already implemented a TextManipulator class that supports manipulation of a TextShape.
~cpp Manipulator* TextShape::CreateManipulator () const { return new TextManipulator (this);
The object adapter uses object composition to combine classes with different interfaces. In this approach, the adapter TextShape maintains a pointer to TextView.
~cpp class TextShape : public Shape { public: TextShape (TextView*); virtual void BoundingBox (Point& bottomLeft, Point& topRight) const; virtual bool IsEmpty () const; virtual Manipulator* CreateManipulator () const; private: TextView* _text; };
TextShape must initialize the pointer to the TextView instance, and it does so in the constructor. It must also call operations on its TextView object whenever its own operations are called. In this example, assume that the client creates the TextView object and passes it to the TextShape constructor.
~cpp TextShape::TextShape (TextView* t) { _text = t; } void TextShape::BoundingBox ( Point& bottomLeft, Point& topRight ) const { Coord bottom, left, width, height; _text->GetOrigin (bottom, left); _text->GetExtent (width, height); bottomLeft = Point (bottom, left); topRight = Point (bottom + height, left + width); } bool TextShape::IsEmpty () const { return _text->IsEmpty (); }
CreateManipulator's implementation doesn't change from the class adapter version, since it's implemented from scratch and doesn't reuse any existing TextView functionality.
~cpp Manipulator* TextShape::CreateManipulator () const { return new TextManipulator (this); }
Compare this code the class adapter case. The object adapter requires a little more effort to write, but it's more flexible. For example, the object adapter version of TextShape will work equally well with subclasses of TextView -- the client simply passes an instance of a TextView subclass to the TextShape constructor.
1.12. Related Patterns ¶
BridgePattern ์ adapter object์ ๋น์ทํ ๊ตฌ์กฐ๋ฅผ ๊ฐ์ง๋ค. ํ์ง๋ง BridgePattern์ AdapterPattern๊ณผ ๊ทธ ์๋๊ฐ ๋ค๋ฅด๋ค. BridgePattern์ ์ค์ ๊ตฌํ๋ถ์ interface๋ถ๋ถ์ ๋ถ๋ฆฌ์์ผ ์ค์ ๊ตฌํ ๋ถ๋ถ์ด ๋ค์ํ๊ณ ๋
๋ฆฝ์ ์ผ ์ ์๋๋ก ํ๊ธฐ ์ํ ๊ฒ์ด๋ค. adapter๋ ํ์ฌ ์ด๋ฏธ ์กด์ฌํ๋ ๊ฐ์ฒด์ ๋ํ interface๋ฅผ ๋ฐ๊พธ๊ธฐ ์ํด ์ด์ฉ๋๋ค.
DecoratorPattern์ ๊ฐ์ฒด์ ๋ํ ์ธํฐํ์ด์ค์ ๋ณํ์์ด ๊ฐ์ฒด๋ฅผ ํ์ฅ์ํจ๋ค. Decorator ๋ adapter๋ณด๋ค ๋ application์ ๋ํด ํฌ๋ช
์ ์ด๋ค. ๊ฒฐ๋ก ์ ์ผ๋ก DecoratorPattern์ ์ฌ๊ท์ ์ธ composition์ ์ ๊ณตํ๋ค. ์ด๊ฒ์ ์์ํ adapter๋ก์๋ ๋ถ๊ฐ๋ฅํ๋ค.
ProxyPattern์ ํด๋น ๊ฐ์ฒด์ ๋ํ ๋๋ฆฌ์ ์ญํ ์ ํ๋ฉฐ, ์ค์ ๊ฐ์ฒด์ ๋ํ interface๋ฅผ ๋ณํ์ํค์ง ์๋๋ค.