어댑터 패턴은 실생활에서 사용하는 어댑터와 동일한 역할을 수행한다.

한 클래스의 인터페이스를 클라이언트에서 사용하고자 하는 다른 인터페이스로 변환한다.

 

어댑터 패턴 사용시 라이브러리를 직접 사용하는 것이 아닌, 

라이브러리를 캡슐화한 어댑터를 사용하게 되므로, 소스간 결합도가 약해지는 장점이 있다.

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

반응형

+ Recent posts