Adapter Pattern 은 Wrapper 라고도 불리는 패턴으로, Wrapping 과 같은 방법을 사용해서 서로 다른 일을 하게 된 두 interface 역할체들을 묶어주는 일을 해줄 때 사용하는 패턴이다. 현실에서 아답터처럼, 제공해주는 interface 를 다른 interface 와 연결해준다는 의미를 가지고 있다고 보면 된다.
Adapter Pattern 에서는 Client 가 기대하고 있고 호출하는 interface 를 Target Interface 라고 한다. 아답터는 이 호출을 다른 인터페이스 측의 함수를 호출하게 되는데, 이 인터페이스는 Adaptee Interface 라고 한다. Client 는 이 Adapter 의 존재를 모른채 호출을 하기 때문에 Interface 연동성에 큰 도움을 주는 패턴이다.
전략 패턴에서 사용하였던 Duck 역할체와 (그 때는 abstract 긴 했지만) 다른 일을 수행하도록 하는 Turkey 역할체가 있다고 가정하고, 다음과 같이 하나씩 구현체가 있다고 해보겠다.
public interface Duck {
public void quack();
public void fly();
}
public class MallardDuck implements Duck {
@Override
public void quack(){
System.out.println("Quack");
}
@Override
public void fly(){
System.out.println("Doing Mallard Duck Fly!");
}
}
public interface Turkey {
public void gobble();
public void fly();
}
public class WildTurkey implements Turkey {
@Override
public void gobble(){
System.out.println("Gobble");
}
@Override
public void fly(){
System.out.println("Doing Turkey Fly!");
}
}
위와 같은 상황이 있을 때, 경우에 따라서 Turkey 라는 역할체에서 하는 일을 수행할 때 해당 일이 아닌 Duck 역할체의 일을 수행하게 하도록 할 수 있다. 물론 인터페이스를 바꾸는게 best 지만, 기존 인터페이스를 바꾸지 않는게 나은 case 도 있기 때문이다. 이 때 다음과 같은 Adapter Class 를 만들어 볼 수 있다.
public class TurkeyAdapter implements Duck {
private Turkey turkey;
public TurkeyAdatper(Turkey turkey){
this.turkey = turkey;
}
@Override
public void quack(){
this.turkey.gobble();
}
@Override
public void fly(){
for(int i = 0; i < 5; i++){
this.turkey.fly();
}
}
}
Adapter Pattern 을 적용할 때는 Target / Adaptee Interface 를 잘 생각하며 만들어주는게 좋다. Client 가 요구할 인터페이스는 Duck 인터페이스이다. Client 는 Duck 이 수행할 수 있는 기능을 호출하고 싶은 것이다. 그리므로 Adaptee 는 Turkey 인터페이스가 된다. 다음과 같이 수행을 시켜볼 수 있다.
public static void main(String [] args){
MallardDuck duck = new MallardDuck();
WildTurkey turkey = new WildTurkey();
// 사용하고 싶은 아답터를 가져온다
// 당연히 구현체를 직접 넣어주거나, 구현체로 매핑되어 있는 역할체를 넣어줘야 한다
Duck duckButTurkey = new TurkeyAdapter(turkey);
System.out.println("Mallard Duck ---------");
duck.quack();
duck.fly();
System.out.println("Wild Turkey ----------");
turkey.gobble();
turkey.fly();
System.out.println("Duck Interf but implementing Turkey ----------")
duckButTurkey.quack();
duckButTurkey.fly();
}
위와 같이 Target 역할체로 취급받지만 Adaptee 구현체의 일을 할 수 있는 패턴이다. 단, 위에서 gobble과 quack, 각각의 fly 함수들처럼 논리적으로 매핑할 수 있을만한 역할체들 사이를 연결해야 비즈니스 로직에 문제가 생기지 않을 것이다.
<병합된 사진>
Adapter Pattern 은 방금 살펴본 바와 같이 Interface 를 연결해주는데 흔하게 사용되지만, 두번째 UML 처럼 Inheritance 관계에서도 사용될 수 있다. 이 때는 Adaptee 와 Target 이 같은 Adapter Class 를 상속받아야 한다는 제약이 있다.
또 다른 예시로 Java 에서는 Enumeration 과 Iterator 라는 대표적인 두 인터페이스가 있다. 이 때, 두 인터페이스를 서로 Adapter 로 연결해서 사용할 수 있는 예제를 만들어보자.
Enumeration - for Vector, Stack, HashTable ...
- hasMoreElements()
- nextElement()
Iterator
- hasNext()
- next()
- remove()
public class EnumerationIterator implements Iterator {
// Target 이 수행해줄 Adaptee
private Enumeration enumeration;
public EnumerationIterator(Enumeration enumeration){
this.enumeration = enumeration;
}
@Override
public boolean hasNext(){
return this.enumeration.hasMoreElements();
}
@Override
public Object next(){
return this.enumeration.nextElement();
}
@Override
public void remove(){
throw new UnsupportedOperationException();
}
}
위와 같이 Adapter 를 만들어주면 Enumeration 으로 가져온 자료구조들에 Iterator 를 사용해줄 수 있다. 다양한 Collection 에 대해서 Iterator 를 활용하는 Application 이 있을 때, Enumeration 을 갖는 자료구조 또한 Adapter Pattern 을 적용하면 쉽게 기능성을 확장시킬 수 있다.
정리하며
Adapter Pattern 은 어느정도 연관성이 있는 두 Interface 를 연결하여 Client 가 보다 편하게 Interface 들을 다룰 수 있도록 도와주는 패턴이다. 위 EnumerationIterator 처럼, 어플리케이션에서 유용하게 쓰일만한 상황인지에 대한 정확한 판별이 필요하기도하다. 무분별한 사용은 오히려 인 앱 로직을 복잡하게 만들 것이고 나중에 유지 / 보수를 오히려 어렵게 만들 수도 있기 때문이다 (뭐 따지고 보면 모든 디자인 패턴이 그렇다).
위와 같은 상황이 많겠냐 싶을 수는 있겠지만, 개인적으로는 오히려 앱 내에서 [크고 자주 사용되는 인터페이스] 가 [특정하고 작은 인터페이스] 에 대하여 사용성을 주고 싶을 때 유용하게 사용될 수 있을 것 같다.
출처
1) 에릭 프리먼, 『Head First Design Patterns』, O'Reilly Media (2004)
2) 중앙대학교 소프트웨어학부 이찬근 교수님 디자인 패턴 수업자료 중
'SW 설계 > Design Pattern' 카테고리의 다른 글
[Design Pattern] Structural - 브릿지 패턴 (Bridge Pattern) (0) | 2023.10.22 |
---|---|
[Design Pattern] Structural - 컴포지트 패턴 (Composite Pattern) (0) | 2023.10.22 |
[Design Pattern] Structural - 데코레이터 패턴 (Decorator Pattern) (1) | 2023.10.21 |
[Design Pattern] Creational - 싱글톤 패턴 (Singleton Pattern) (1) | 2023.10.21 |
[Design Pattern] Creational - 빌더 패턴 (Builder Pattern) (0) | 2023.10.21 |