목차
- 개요
- 요약
- 문제상황
- 어댑터 패턴을 통한 해결
- 객체 어댑터와 클래스 어댑터
- 결론
개요
본 글은 head first design patterns 책을 읽고 정리한 글입니다.
어댑터 패턴은 다른 인터페이스로 기존 클래스의 인터페이스를 변환하는 패턴입니다. 이는 호환성 문제를 해결할 수 있습니다. 호환성 문제는 클래스의 인터페이스가 클라이언트의 요구사항과 맞지 않을 때 발생합니다.
이때 어댑터 패턴을 사용하면 클라이언트의 요구사항에 맞게 클래스의 인터페이스를 변환할 수 있습니다. 마치 한국 콘센트를 유럽 콘센트에 맞게 변환하는 것과 같습니다.
head first design patterns는 이에 대한 예시로 전에 사용했던 오리를 가져옵니다. 어쩌다보니 오리가 모자라서 칠면조를 사용해야 상황이 왔는데, 칠면조는 오리와는 다른 울음 소리를 내고 있습니다. 이때 칠면조의 목소리를 오리와 동일하게 변환하는 어댑터를 사용하면 칠면조를 오리로 사용할 수 있습니다.
어댑터를 사용해서 기존에 사용하던 오리 코드를 수정하지 않고 칠면조를 오리로 사용하면 코드의 수정이 최소화됩니다. 이는 유연성과 재사용성을 높일 수 있습니다.
어댑터 패턴 구현 방식은 클래스 어댑터와 객체 어댑터로 나뉩니다. 클래스 어댑터는 상속을 통해 어댑터를 구현하고, 객체 어댑터는 구성을 통해 어댑터를 구현합니다.
요약
- 어댑터 패턴은 클래스의 인터페이스를 다른 인터페이스로 변환하는 패턴입니다.
- 호환성 문제를 해결할 수 있습니다.
- 기존의 코드를 수정하지 않고 사용할 수 있어 유연성과 재사용성을 높일 수 있습니다.
- 어댑티 - 칠면조, 어댑티 - 오리, 클라이언트 - 사용자
- 구현 방식에 따라 클래스 어댑터와 객체 어댑터로 나뉩니다.
문제상황
동물원에서 기존에 오리만으로 이루어진 공연이 있었습니다. 그런데 어느 날 몇 마리의 오리가 아프게 되어서 공연을 할 수 없게 되었습니다. 그래서 동물원은 칠면조를 대신해서 공연을 하기로 했습니다. 그런데 오리가 “꽥꽥” 소리를 내는데 반해 칠면조는 “고블고블” 소리를 내기 때문에 공연을 할 수 없었습니다.
위와 같은 상황을 코드로 나타내면 다음과 같습니다.
public interface Duck {
public void quack();
public void fly();
}
public interface Turkey {
public void gobble();
public void fly();
}
이런 상황에서 칠면조를 오리로 사용하는 방법이 필요합니다. 칠면조의 “고블고블” 소리가 오리의 “꽥꽥” 소리와 동일하게 동작하도록 만드는 것이죠.
어댑터 패턴을 통한 해결
이런 상황에서 어댑터 패턴을 사용하면 칠면조를 오리로 사용할 수 있습니다. 어댑터 패턴을 사용하면 칠면조의 “고블고블” 소리를 오리의 “꽥꽥” 소리로 변환할 수 있습니다.
이를 코드로 나타내면 다음과 같습니다.
public class TurkeyAdapter implements Duck {
Turkey turkey;
public TurkeyAdapter(Turkey turkey) {
this.turkey = turkey;
}
public void quack() {
turkey.gobble();
}
public void fly() {
turkey.fly();
}
}
이렇게 어댑터 패턴을 사용하면 칠면조를 오리로 사용할 수 있습니다. 칠면조의 “고블고블” 소리는 어댑터를 통해 “꽥꽥” 소리로 변환되어 공연 연출가에게 전달됩니다.
이런 방식으로 어댑터 패턴을 사용하면 기존의 코드를 수정하지 않고 칠면조를 오리로 사용할 수 있습니다. 이는 유연성과 재사용성을 높일 수 있습니다. 동물원의 입장에서는 새로운 오리를 추가로 구매할 필요가 없어지고, 칠면조의 입장에서는 꽥꽥 소리를 내는 훈련을 할 필요가 없어지는 겁니다. 애초에 불가능하겠지만요.
객체 어댑터와 클래스 어댑터
위와 같이 인터페이스를 통해 어댑터를 구현하는 것을 객체 어댑터라고 합니다. 객체 어댑터는 인터페이스를 구현하는 클래스에서 어댑터를 통해 변환할 객체를 소유하는 것을 통해 요구되는 인터페이스를 제공합니다.
또 다른 방법으로는 다중 상속을 통해 어댑터를 구현할 수도 있습니다. 이를 클래스 어댑터라고 합니다. 클래스 어댑터는 변환 대상과 변환 결과를 동시에 상속받아서 변환을 수행합니다. 오리와 칠면조를 교배시켜 만든 새로운 종류의 새라고 생각하시면 됩니다.
클래스 어댑터를 코드로 나타내면 다음과 같습니다.
class TurkeyAdapter : public Duck, public Turkey {
public:
void quack() {
gobble();
}
void fly() {
Turkey::fly();
}
void gobble() {
cout << "Gobble gobble Quack quack" << endl;
}
}
결론
어댑터 패턴은 클래스의 인터페이스를 다른 인터페이스로 변환하는 패턴입니다. 이를 통해 호환성 문제를 해결하면 기존의 코드를 수정하지 않고 사용할 수 있어 유연성과 재사용성을 높일 수 있습니다. 이는 오리만으로 이루어지던 공연에 칠면조를 추가할 수 있게 되는 것과 동일한 맥락입니다.
어댑터 패턴을 구현하는 방법은 객체 어댑터와 클래스 어댑터로 나눌 수 있습니다. 객체 어댑터는 인터페이스를 구현하는 클래스에서 어댑터를 통해 변환할 객체를 소유하는 것을 통해 요구되는 인터페이스를 제공합니다. 클래스 어댑터는 변환 대상과 변환 결과를 동시에 상속받아서 변환을 수행합니다. 이는 칠면조의 입에 소리 변환기를 달아주는 것과 칠면조와 오리를 교배시켜 새로운 종류의 새를 만드는 것과 비슷하게 생각하시면 됩니다.