어댑터 패턴은 실생활에서 사용하는 어댑터와 동일한 역할을 수행한다.
한 클래스의 인터페이스를 클라이언트에서 사용하고자 하는 다른 인터페이스로 변환한다.
어댑터 패턴 사용시 라이브러리를 직접 사용하는 것이 아닌,
라이브러리를 캡슐화한 어댑터를 사용하게 되므로, 소스간 결합도가 약해지는 장점이 있다.
wrapper 와 slf4j(log4j 의 어댑터) 는 어댑터라 볼 수 있다.
[Adapter Pattern의 Class 다이어그램(참고)]
Client 는 Adapter 구상 클래스를 Target Interface를 통해 사용한다.
Adapter 클래스는 Target Interface를 구현한다.
Adapter 클래스는 Adaptee(새롭게 사용할 구상 클래스) 를 구상 클래스로 사용하며,
Adapter의 Request()는 Adaptee의 SpecificRequest()를 호출한다. (Adaptee를 캡슐화)
Client 는 Adapter 의 Request()를 사용하는 줄 알지만 실제론 Adaptee 의 SpecificRequest()를 사용하게 된다.
실제 Adaptor pattern 의 사용 예를 살펴보자.
1-1. Adapter 패턴을 사용하지 않은 설계
[OldLibraryIF]
1
2
3
|
public interface OldLibraryIF {
public void sendPush(String input);
}
|
cs |
[OldLibrary]
OldLibrary 인터페이스를 구현하는 클래스
1
2
3
4
5
6
7
|
public class OldLibrary implements OldLibraryIF {
public void sendPush(String input) {
System.out.println("send " + input + " using OldLibrary");
}
}
|
cs |
[Client]
OldLibrary를 사용하는 클라이언트
1
2
3
4
5
6
|
public class Client {
public static void main(String[] args) {
OldLibraryIF oldLib = new OldLibrary();
oldLib.sendPush("message");
}
}
|
cs |
[결과]
send message using OldLibrary
위와 같이 모든 구현이 끝나있는 상태에서 기존의 라이브러리였던 OldLibrary 를 제거하고,
아래와 같은 새로운 라이브러리(NewLibrary)를 사용해야 하는 상황이 생긴다면 어떻게 해야할까?
교체대상인 OldLibrary를 사용하는 클라이언트를 일일히 찾아 수정해주어야 한다.
1-2. Adapter 패턴을 사용하지 않은 설계에서 OldLibrary를 NewLibrary로 교체
[NewLibraryIF]
1
2
3
4
|
public interface NewLibraryIF {
public void sendMsg(String input);
}
|
cs |
[NewLibrary]
NewLibrary 인터페이스를 구현하는 클래스
1
2
3
4
5
6
7
|
public class NewLibrary implements NewLibraryIF {
public void sendMsg(String input) {
System.out.println("send " + input + " using NewLibrary");
}
}
|
cs |
[Client]
OldLibrary 대신 NewLibrary를 사용하도록 기존의 Client 를 수정.
1
2
3
4
5
6
7
|
public class Client {
public static void main(String[] args) {
NewLibraryIF newLib = new NewLibrary();
newLib.sendMsg("message");
}
}
|
cs |
[결과]
send message using NewLibrary
라이브러리 교체는 쉽게 끝이난 듯 보인다.
하지만 OldLibrary를 사용하는 곳이 수십, 수백곳이라면?!
애초에 어댑터 패턴을 사용했더라면 어땟을까?
2-1. Adapter 패턴을 사용한 설계
[Adapter]
OldLibrary를 캡슐화한 Adapter 클래스
1
2
3
4
5
6
7
8
9
10
|
public class Adapter implements OldLibraryIF{
OldLibraryIF oldLibrary = new OldLibrary();
@Override
public void sendPush(String input) {
oldLibrary.sendPush(input);
}
}
|
cs |
[Client]
1
2
3
4
5
6
|
public class Client {
public static void main(String[] args) {
OldLibraryIF adapter = new Adapter();
adapter.sendPush("message");
}
}
|
cs |
[결과]
send message using OldLibrary
Client 에서 OldLibrary를 생성자로 직접 생성하여 사용하는 대신, Adapter 클래스를 사용했다.
이전과 마찬가지로 OldLibrary를 NewLibrary로 교체해보자.
2-2. Adapter 패턴을 사용한 설계에서 OldLibrary를 NewLibrary로 교체
[Adapter]
OldLibrary를 멤버변수로 사용하는 대신 NewLibrary를 멤버변수로 사용
1
2
3
4
5
6
7
8
9
10
11
|
public class Adapter implements OldLibraryIF{
NewLibraryIF newLibrary = new NewLibrary();
@Override
public void sendPush(String input) {
newLibrary.sendMsg(input);
}
}
|
cs |
[Client]
기존의 Client 코드와 동일한 코드를 사용해도 된다.
Adapter 클래스 코드만 수정했을 뿐, Client 코드는 바꾼게 없다.
1
2
3
4
5
6
7
|
public class Client {
public static void main(String[] args) {
OldLibraryIF adapter = new Adapter();
adapter.sendPush("message");
}
}
|
cs |
[결과]
send message using NewLibrary
이와 같이 Adapter 패턴(캡슐화)을 사용하게 되면 라이브러리의 변화가 발생해도 클라이언트를 수정하지 않아도 된다.
도서 <Clean Code> 에도 외부라이브러리를 캡슐화 하여 사용하는 이점에 대해 언급되어있다.
참고 : Head First Design Patterns
'back > Design Pattern' 카테고리의 다른 글
[Design Pattern] 정적 팩토리 메소드 패턴 (0) | 2022.11.03 |
---|---|
[Design pattern] Factory pattern (팩토리 패턴) (0) | 2020.01.30 |
[Design pattern] Builder pattern (빌더 패턴) : 객체를 정의하는 세가지 패턴 (0) | 2019.12.24 |
[Design pattern] Proxy pattern (프록시 패턴) (2) | 2019.12.17 |
[Design pattern] Observer Pattern (옵저버패턴) 그리고 리스너 (0) | 2019.11.21 |