Adapter(Wrapper) Pattern
I. 의도
인터페이스가 일치 하지 않는 클래스들을 원하는 인터페이스로 변환해 함께 동작 시킬 수 있도록 한다.
어댑터 패턴은 데코레이터나 프락시 브릿지 패턴을 이해하는데 도움이 되고 비교하여 차이를 알아야 한다.
Opening Question
- 목표 인터페이스와 구현클래스의 인터페이스가 동일하게 어탭터 패턴을 적용해 본 적이 있는가?
그것이 프락시라고 할 수 있나?
그렇다면 프락시와는 어떤 차이점이 있는 것일까? - 어떤 경우에 어탭터가 필요할까?
- 어댑터를 쓰지 말아야 할 경우는?
- Webwork의 Intercepter와 비교해보자.
II. 본론
1) 동기
존재하나 현재 이를 사용하고자 하는 클래스와는 아무런 연관 없이 개발된될 클래스나 서로 일치하지 않는 인터페이스를 갖는 클래스들을 홍합하여 하나의 애플리케이션으로 개발해야할경우 기존 코드를 통합하여 운영하고자 할 때
우리가 선택할 수 있는 항목은 아마 다음 두가지 일 것이다.
- 목표로 하는 인터페이스와 구현이 되어 있는 클래스 둘 다 상속받는 방법
- 구현 클래스를 포함 시키고 목표로 하는 인터페이스 관점에서 구현하는 것 : 집합관계(Aggregation)을 이용하여 해결
- 또 무엇이 있을까? Mixin? lamda function?
첫번재 방법을 클래스로 구현하였다.
두번째 방법을 객체로 구현하였다. 라고 표현한다.
동작방식
- BoundingBox() 오퍼레이션의 구현을 보면, 실제 구현을 제공할 TextView 클래스에 정의된 GetExtent() 메소드를 호출하도록 바꾸어 TextView에 전달한다.: Delegation
-
기존 클래스가 제공하지 않는 기능성을 제공해야할 경우도 있다. 추가 기능을 처리하는 행위를 추가로 정의함으로 가능하게 된다. (Decoration과 어떤 차이?)
교재의 구현방식은 Factory Method도 사용되었다.
2) 활용성
- 기존 클래스를 사용해야 하나 인터페이스가 수정되어야 하는 경우
- 이미 만들어진 것을 재사용하고자 하나 재사용 가능한 라이브러리를 수정할 수 없는 경우
- 여러 개의 서브 클래스를 사용해야하는 데 이 서브클래스들의 상속을 통해서 이들의 인터페이스를 다 adaptation 한다는 것이 현실성이 없다면 객체 어댑터 방식으로 부모 클래스의 인터페이스를 변형하는 것이 더 바람직 하다 : lucy 라이브러리
4) 구조
Java 에서는 다중 상속을 지원하지 않기 때문에 1번 방식은 사용할 수 없다. 물론 내부적으로 델리게이션을 사용하는 것은 동일하다.
- 다중 상속을 이용한 Adapter 패턴 : super.SpecificRequest()
-
객체 합성에 의한 Adapter 패턴
: adaptee.SepecificRequest()
5) 참여 객체
- Client : 기능 사용자
- Target : 목표로 하는 인터페이스
- Adaptee : 기능을 가지고 있는 객체
- Adapter : Aaptee의 기능을 가지고 Target의 인터페이스 구현한 객체
6) 협력 방법
클라이언트는 어댑터에 해당하는 클래스의 인스턴스에게 요청 메세지를 전달하고 어댑터는 변형되는 클래스의 오퍼레이션을 호출한다.
7) 결과
2가지 구현방식의 장단점
-
클래스 어댑터
- Adapter는 명시적으로 Adaptee를 상속받고 있을 뿐 Adaptee의 서브클래스들을 상속받는 것은 아니므로, Adaptee의 서브클래스에 정의된 기능들을 사용할 수 없다.
- Adaptee에 정의 된 행위(오퍼레이션)을 재정의 할 수 있다.
- 한 개의 객체만 만들어 낸다. Adapter 객체, Adaptee에 대한 참조자도 필요하지 않다. : 어떤 의미가 있을까? 관리객체수가 적다. 코딩수가 적다.
-
객체 어댑터
- 다양하고 많은 Adaptee 클래스와 동작할 수 있다. 하나의 Adapter 클래스로 모든 Adaptee 클래스와 이를 상속받는 서브클래스 모두를 이용할 수 있다.
- Adaptee 클래스 오퍼레이션 재정의는 어렵다. Adaptee 클래스를 상속받은 새 서브클래스를 만들고 이 서브클래스를 참조하도록 해야 한다.
고려사항
-
adapatation를 위해 수행해야할 일은 어느 정도일까? 구현할 양의 결정 요인은 Target 인터페이스와 Adaptee 간의 얼마만큼의 유사성을 갖는가? 이다.
- 가장 단순한 인터페이스 변환, 새로운 오퍼레이션 추가는 복잡한 케이스
-
Pluggable Adapter
- 내가 개발한 클래스를 사용할 모든 클라이언트에게 동일한 인터페이스를 제공해야 한다는 가정을 배제 가능
하나의 클래스를 설계할 때 모든 클라이언트가 원하는 표준화된 인터페이스를 정의해야한다는 부담을 덜 수 있다. - 인터페이스 Adaptation를 담당하는 클래스를 대체할수 있는 어댑터( Pluggable Adapter) 라고 했다.
- 내가 개발한 클래스를 사용할 모든 클라이언트에게 동일한 인터페이스를 제공해야 한다는 가정을 배제 가능
-
양방향 어댑터
- 모든 클라이언트에 적용되지 못한다는 것
- 서로 다른 두 개의 클라이언트가 객체를 서로 다르게 바라봐야 할 때
- Adaptation되는 두클래스 모두의 인터페이스를 다 상속 받아 정의 하도록 한다 : Composite와 유사?
구현
-
클래스 어댑터 방식의 C++
- Target : public 상속
- Adaptee : private 상속
- 양방향 어댑터는 아니구나~
-
Pluggable Adapter : 3가지 구현 방식
-
- 세 가지 방법 모두에 공통, Adapation가 필요한 오퍼레이션의 최소 집합
-
구현방식으로 추상 오퍼레이션을 제공하는 방법
- Adaptation 인터페이스를 추상 오퍼레이션으로 정의하고 상속받는 서브클래스는 추상 오퍼레이션에 대한 구현을 제공해야 하고 계층 구조를 갖는 객체를 Adaptation할 수 있다.
-
Delegate 객체를 사용한다.(집합관계)
- 자신에게 요청된 메시지를 다른 위임 개체에게 전달, 여러 가지 다른 Adaptation 전략을 수사할 수 있다.
- 트리 구조를 계층적 디렉토리 구조로 Adaptation하는 위임자 역할 수행
-
파라미터화된 어댑터
-
-
어댑터의 파라미터화를 통하여 대체 가능한 어댑터를 만들 수 있다.
-
파라미터가 될 수 있는 블록 요소
- directoryDisplay := (TreeDisplay on: treeRoot) getChildrenBlock: [:node | node getSubdirectories] createGraphicNodeBlock: [:node | node createGraphicNode]
-
루비 코드
- dirctoryDisplay = TreeDisplay.treeRoot;
- dirctoryDisplay.getChildrenBlockcreateGraphicNodeBlock({node| node.getSubdirectories},{node| node.createGraphicNode});
인터페이스 Adaptation을 클래스로 만든다면 서브클래싱보다 더 편한 방법이다.
-
8) 관련 패턴
-
Bridge 패턴
- 객체 어댑터와 클래스 구조가 유사하다
- 사용 목적이 다르다
- 구현과 추상 개념(인터페이스)를을 분리하여 서로 영향을 주지 않고 각각을 확장하려 하는 것
- 어댑터는 존재하는 객체의 인터페이스를 변경
-
Decorator 패턴
- 인터페이스 변경 없이도 객체에 새로운 행위를 추가할 수 있다.
- 어댑터 보다는 애플리케이션을 위해 좋은 방법
-
순수한 어댑터로는 불가능한 재귀적 합성이 가능
-
Proxy 패턴
- 인터페이스를 변경하는 책임이 없다.
'direct' 카테고리의 다른 글
| Opera를 설치했습니다. (0) | 2008/03/28 |
|---|---|
| Adapter Pattern (2) | 2008/03/27 |
| 뭉쳐져 있는 파라미터와 펼쳐져 있는 파라미터 해결은? (0) | 2008/03/24 |
| Java와 Keyword Parameter : map을 이용한 파라미터 전달 혹은 수많은 파라미터를 넘겨줄 것 인가? (5) | 2008/03/24 |

