Notice
Recent Posts
Recent Comments
Link
이런거 저런거 그런거
어댑터(Adaptor) 패턴 본문
클래스의 인터페이스를 클라이언트에서 요구하는 다른 인터페이스로 변환합니다.
인터페이스가 호환되지 않아 쓸 수 없었던 클래스들을 같이 사용 할 수 있게 해 줍니다.
Head First 디자인패턴 p.308
|
- Vendor Class : Existing System이 사용하는 인터페이스를 지원하지 않는 클래스
- Adapter : Vendor Class가 Existing System이 사용하는 인터페이스를 지원하는 것처럼 보이게 해 주는 클래스
따라서 어댑터에서는
- 기존 시스템(클라이언트)에서 사용하는 인터페이스를 구현한다.
- 인터페이스 구현은 벤더클래스에서 제공하는 인터페이스를 이용하여 구현한다.
역할만 보자면 C에서 간혹 구현해서 사용하는 Wrapper 함수와 유사하다.
그래서 Wrapper패턴으로 불리기도 한다.
GoF디자인 패턴의 분류에 따르면 구조(Structural) 패턴에 속한다.
※ 구조(Structural) 패턴
클래스나 객체를 조합해 더 큰 구조를 만드는 패턴
디자인 패턴 Card | Head First (p.281) |
- Adaptee : (Client가) 원하는 인터페이스를 지원하지 않는 클래스
- Target : Client 동작에 필요한 인터페이스. 즉, Adaptee가 지원하길 바라는 인터페이스
- Adapter : Adaptee가 Target인터페이스를 지원하는 것 처럼 보이게 해 주는 클래스
어떤 경우에 사용 ?
- 기존 시스템(클라이언트)과 벤더클래스 수정 없이 둘 사이를 결합시켜 동작 시킬 수 있다.
사용하면 어떤 장점이 ?
- 클라이언트와 벤더클래스 간 의존성을 줄일 수 있어 결합도를 낮추고 응집도가 높은 설계가 가능하다.
- 결합도를 낮춤 : 한쪽의 변경이 다른 한쪽에게 영향을 주지 않음
- 응집도가 높음 : 클라이언트/어댑터/어댑티 각각의 역할이 구분되어 각자의 기능에만 집중하여 구현 가능
어떻게 ?
- 의존 역전 원칙(DIP)
- 동작흐름
- 클라이언트 >> 어댑터(인터페이스) >> 어댑터(구상클래스) >> 벤더클래스
- 의존성흐름
- 클라이언트 >> 어댑터(인터페이스) << 어댑터(구상클래스) >> 벤더클래스
따라서 클라이언트는 반드시 인터페이스에 맞춰서 프로그래밍되어야 한다.
※ "디자인원칙 : 특정 구현이 아닌, 인터페이스에 맞춰서 프로그래밍한다." (p.113)
책 예제(p.276)
- 클라이언트(오리) > 물오리 (가능)
- 클라이언트(오리) > 야생칠면조 (불가)
클라이언트에서 야생칠면조를 사용가능하게 하기 위해 칠면조어댑터를 추가하였다.
- 클라이언트(오리) > 칠면조어댑터 > 야생칠면조
더보기
이런 예제라면 어떨까?
- 클라이언트(USB) > USB키보드 (가능)
- 클라이언트(USB) > PS/2키보드 (불가)
- 클라이언트(USB) > PS/2 USB어댑터 > PS/2키보드
따라서 클라이언트에서는 어댑티의 존재를 알지 못한다.
바보같은 질문은 없습니다(p.280) Q) 어댑터에서 얼마나 적응을 시켜줘야하나요? 대형 타겟 인터페이스를 구현해야 되는 경우에는 할 일이 정말 많아질 수 있을 것 같은데요? > 어댑터 구현 양은 인터페이스 크기에 비례함. 따라서 많은 양인 경우 어댑터 없는 설계를 고려해보아도 좋음 Q) 한 어댑터에서 한 클래스만 감싸야 하나요? > 경우에 따라 두 개 이상의 어댑티를 감싸야 하는 상황이 생길 수도 있음(ex> Q) ... 시스템(Client) 내에 어댑티를 이용하는 방법이 2종류라면?
> 즉, 어댑터에는 본래 어댑터 기능에 더해 최신 구현부분을 위한 인터페이스도 추가 > (이 경우 최신 구현부분의 기능은 바이패스역할정도일듯) |
어댑터 구현 방식 (p.281)
객체 (Object) 어댑터 |
설명 | 어댑티를 적응시키는데 있어서 구성을 사용하는 방식 |
장점 | - 어댑티의 모든 서브클래스에 대해 어댑터 적용이 가능하다. - 실행 중에 동적으로 어댑티를 변경 할 수 있다. (스트래티지 패턴) - 어댑티와 어댑터 객체화 시점이 달라도 된다. |
|
단점 | - 어댑티 객체를 별도로 생성하고 등록하는 과정이 필요하다. (관리 할 객체 및 코드 증가) | |
클래스(Class) 어댑터 |
설명 | 어댑티를 적응시키는데 있어서 상속을 사용하는 방식 |
장점 | - 어댑터가 어댑티의 서브클래스라 어댑티의 행동을 오버라이드 할 수 있다. - 어댑티 객체를 만들 필요 없다. (코드 단순화) |
|
단점 | - 상속을 사용하기에 어댑터 클래스는 특정 어댑티 클래스에만 적용 가능하며 컴파일시 고정된다. - 어댑티는 구상클래스여야 하며 동적으로 변경 불가하다. |
※ 클래스 어댑터의 다중 상속 여부에 따른 사용 제약
"Head First"책에는 다중 상속이 가능한 경우(e.g. C++)에만 클래스 어댑터 사용가능하다고 쓰여져있지만, 어댑티는 상속하여 구현하고 Target으로부터는 인터페이스로 구현하면 다중 상속이 불가능한 Java에서도 사용 가능함.
※ 스트래티지 패턴
행위를 클래스로 캡슐화해 동적으로 행위를 자유롭게 바꿀 수 있게 해주는 패턴
커맨드 패턴과의 유사성
어댑터 패턴 (구조 패턴) | 커맨드 패턴 (행위 패턴) |
행위 (Behavioral) 패턴
객체나 클래스 사이의 알고리즘이나 책임 분배에 관련된 패턴
작업을 여러 객체들에 대해 분배하면서 서로간의 의존성을 최소화
어댑터 패턴 | SW 변경 없이 클래스 추가만으로 인터페이스 호환을 맞추기 위한 패턴 |
커맨드 패턴 | 행위를 캡슐화 함으로서 주어진 여러 기능들을 실행 할 수 있는 재사용성이 높은 클래스를 설계하는 패턴 |
데코레이터 패턴과의 차이점
비슷하게 볼 수 있는 부분
- 클래스를 감싸고 있는 부분을 구현함으로서 기존 코드를 수정하지 않고 사용 할 수 있도록 한다.
차이점
- 데코레이터
- 목적 : SW 변경 없이 기능(행동)을 유연하게 추가 시켜주기 위한 목적
- 즉, 인터페이스를 변환시키지 않고 행동만 더 함 (Client obj A() -> DecoA.A() -> DecoB.A() -> Obj.A())
- Client에서는 데코레이터의 존재를 모름
- 어댑터
- 목적 : SW 변경 없이 중간에서 인터페이스를 변경하여 호환시켜주기 위한 목적
- 행동은 추가하지 않고 인터페이스만 변화시킴(Client obj A() -> Adap.A() -> Obj.B())
- Client에서는 어댑티의 존재를 모름
커맨드 패턴에 나오는 홈 오토메이션 리모컨 디자인에서
터치리모컨을 추가 해 보는 예
사용예) Android ArrayAdapter
https://developer.android.com/reference/android/widget/ArrayAdapter
References
Head First Design Patterns 스토리가 있는 패턴 학습법, 한빛미디어
https://gmlwjd9405.github.io
https://hamait.tistory.com/364?category=131245
https://www.scribd.com/document/7534738/Design-Patterns-Quick-Reference-Card
Comments