어댑터패턴은 클래스의 인터페이스를 사용자가 기대하는 다른 인터페이스로 변환하는 패턴으로, 호환성이 없는 인터페이스 때문에 함께 동작할 수 없는 클래스들이 함께 작동할 수 있도록 도와준다.
어댑터패턴의 어댑터는 일반적으로 노트북 어댑터의 개념을 떠올리면 된다. 콘센트 전원에서 나오는 전기는 200V이지만, 노트북은 120V이다. 하지만 우리가 노트북을 사용할 때 아무런 불편없이 노트북 충전기를 콘센트에 꽂아 사용할 수 있다. 그 이유는 200V를 120V로 바꿔주는 어댑터가 중간에 있기 때문이다.
즉, 어댑터패턴은 노트북의 어댑터처럼 기존 클래스를 재사용할 수 있도록 중간에서 맞추어주는 역할을 수행한다.
어댑터 패턴에는 2가지 형태가 있다.
1. 클래스 어댑터 패턴(상속)
2. 인스턴스 어댑터 패턴(위임)
위 UML다이어그램은 어댑터 패턴을 나타낸 것으로, Client
는 Target
인터페이스를 사용하여 메서드를 호출한다. Adapter
에서는 Adaptee
인터페이스를 사용하여 concreateMethod호출로 변경한다. 이때, Client
는 중간에 Adapter
가 있다는 것을 인지하지 못한다.
이번 어댑터패턴 구현 예제는 오리와 칠면조는 서로 다른 종류이기 때문에 연결될 수 없지만 어댑터패턴을 이용해 오리가 칠면조의 소리를 낼 수 있도록 구현을 했다.
// Duck.java
public interface Duck {
public void quack();
public void fly();
}
// MallardDuck.java
public class MallardDuck implements Duck {
public void quack() {
System.out.println("꽥! 꽥!");
}
public void fly() {
System.out.println("먼 거리를 날아갈 수 있습니다.");
}
}
UML다이어그램에서 Target
에 해당하는 인터페이스를 정의했다. Duck
은 클래스가 아닌 인터페이스이기 때문에, 상속을 받을 경우 메소드를 구현해야한다.
아래의 MallardDuck
클래스에서 Duck
인터페이스를 상속받고 각 메서드를 구현하였다.
// Turkey.java
public interface Turkey {
public void gobble();
public void fly();
}
// WildTurkey.java
public class WildTurkey implements Turkey {
public void gobble() {
System.out.println("고르륵! 고르륵!");
}
public void fly() {
System.out.println("짧은 거리만 날아갈 수 있습니다.");
}
}
UML다이어그램에서 Adapter
가 구현하는 인터페이스이다. Client
클래스는 Target
인터페이스를 통해 Adaptee
인 서드파티 라이브러리를 사용하게 된다.
[서드파티]
plug in
이나 library
등을 만드는 회사를 의미한다.(개인 개발자나 팀, 혹은 업체에서 만드는 라이브러리)// TurkeyAdapter.java
public class TurkeyAdapter implements Duck {
Turkey turkey;
public TurkeyAdapter(Turkey turkey) {
this.turkey = turkey;
}
public void quack() {
turkey.gobble();
}
public void fly() {
turkey.fly();
}
}
UML다이어그램에서 Adapter
부분을 맡고 있으며, Client
와 Adaptee
중간에 호환성이 없는 둘을 연결시켜주는 역할을 담당한다.
Target
인터페이스를 구현하며, 클라이언트는 Target
인터페이스를 통해 어댑터에 요청을 보낸다. Adapter
는 클라이언트의 요청을 Adaptee
가 이해할 수 있는 방법으로 전달하고 처리는 Adaptee
에서 이루어진다.
// Client.java
public class Client {
public static void main(String[] args) {
System.out.println("칠면조가 웁니다.);
WildTurkey turkey = new WildTurkey();
turkey.gobble();
turkey.fly();
System.out.println("칠면조 어댑터가 웁니다.);
Duck turkeyAdpater = new TurkeyAdapter(turkey);
turkeyAdapter.quack();
turkeyAdapter.fly();
System.out.println("오리가 웁니다.");
MallardDuck duck = new MallardDuck();
duck.quack();
duck.fly();
}
}
Client
는 써드파티 라이브러리나 외부시스템을 사용하는 주체이다.
위 코드를 실행했을 때 Adapter
의 경우 Duck
타입으로 객체를 생성하였지만 어댑터를 통해 칠면조의 울음소리를 내고 짧은 거리를 날 수 있다.
이와 같이 서로 다른 두 가지의 인터페이스를 연결할 때, 어댑터 패턴을 사용하는 것을 확인할 수 있다.