어댑터 패턴은 실생활에서 사용하는 어댑터와 동일한 역할을 수행한다.
한 클래스의 인터페이스를 클라이언트에서 사용하고자 하는 다른 인터페이스로 변환 한다.
어댑터 패턴 사용시 라이브러리를 직접 사용하는 것이 아닌,
라이브러리를 캡슐화한 어댑터를 사용하게 되므로, 소스간 결합도가 약해지는 장점 이 있다.
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]
public interface OldLibraryIF {
public void sendPush(String input);
}
cs
[OldLibrary]
OldLibrary 인터페이스를 구현하는 클래스
public class OldLibrary implements OldLibraryIF {
public void sendPush(String input) {
System .out .println ("send " + input + " using OldLibrary" );
}
}
cs
[Client]
OldLibrary를 사용하는 클라이언트
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]
public interface NewLibraryIF {
public void sendMsg(String input);
}
cs
[NewLibrary]
NewLibrary 인터페이스를 구현하는 클래스
public class NewLibrary implements NewLibraryIF {
public void sendMsg(String input) {
System .out .println ("send " + input + " using NewLibrary" );
}
}
cs
[Client]
OldLibrary 대신 NewLibrary를 사용하도록 기존의 Client 를 수정.
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 클래스
public class Adapter implements OldLibraryIF{
OldLibraryIF oldLibrary = new OldLibrary();
@Override
public void sendPush(String input) {
oldLibrary.sendPush(input);
}
}
cs
[Client]
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를 멤버변수로 사용
public class Adapter implements OldLibraryIF{
NewLibraryIF newLibrary = new NewLibrary();
@Override
public void sendPush(String input) {
newLibrary.sendMsg(input);
}
}
cs
[Client]
기존의 Client 코드와 동일한 코드를 사용해도 된다.
Adapter 클래스 코드만 수정했을 뿐, Client 코드는 바꾼게 없다.
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