1.1. Intent ¶
클래스의 인터페이스를 다른 필요한 클래스의 인터페이스에 맞게 변환해준다. Adapter 는 서로 호환성이 없는 인터페이스들끼리라도 같이 작동할 수 있게끔 해준다.
1.4. Applicability ¶
다음과 같은 경우에 AdapterPattern를 이용한다.
- 이미 만들어져 있는 클래스를 사용하고 싶지만, 인터페이스가 원하는 방식과 일치하지 않을때.
- 관련성이 없거나, 예측하지 못한 클래스들과 협동하는 재사용가능한 클래스를 생성하기 원할때. 이 경우 클래스들이 호환성을 가지는 인터페이스를 필요로 하지 않는다.
- (object adapter 의 경우에만 해당) 현재 이미 만들어진 여러개의 subclass가 필요한 경우, 하지만 각각의 서브클래스들에 대한 인터페이스를 하는 것은 비효율적이다. 이 경우 parent class의 인터페이스를 adapt 할 수 있다.
1.5. Structure ¶
adapter 클래스는 하나의 interface를 다른 interface 에 적합하게 맞춰주기 위해 (말 그대로 어뎁터 역할~) 다중상속을 이용한다.
adapter 객체는 object composition 에 의존한다.
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를 변화시키지 않는다.