유명한 패턴들은 프로그래머들이 당연하게 사용하는 코딩 스타일들이 관습화 된 것일 뿐, 특별하거나 엄청난 개념이 아니다. 어댑터 패턴 또한 그렇다.
다음 그림처럼 콘센트에 코드를 꽂는 경우, 별다른 어댑터가 필요하지 않다.
하지만 외국에 나가게 되면 이런 어댑터가 필요한 경우가 있다.
어댑터 패턴이란.. 바로 이 어댑터를 만드는 행위를 말한다. 프로그래머가 코딩을 하다가 어댑터가 필요한 경우가 뭐가 있을까?
자바 프로그래밍을 예로 들자면, 기존 코드와 다른 인터페이스를 구현하는 클래스를 사용해야 하는 경우 인터페이스가 다르기 때문에 어댑터가 필요하다. (인터페이스가 다른데 막 가져와서 쓸 수는 없지 않은가)
말도 안되는 예를 한 번 들어보겠다.
위에 그림을 보면 K2는 gun 인터페이스를 구현하고 있고 K9은 tank 인터페이스를 구현하고 있다. 우리는 K9 클래스를 사용해야 하는데 인터페이스가 달라서 어댑터 패턴을 사용하고자 한다.
위 그림처럼 어댑터를 생성해서 K9을 기존의 인터페이스로 사용할 수 있게 되는 것이다. 좀 억지 예제지만 이런 비슷한 상황이 온다면 우리는 자연스럽게 어댑터 패턴을 떠올릴 것이고 아마 자신도 모르게 사용하고 있을 것이다.
위 어설픈 예제를 코드로 한 번 구현해보자.
public interface Gun {
public void shoot();
public void load();
}
public class k2 implements Gun {
@Override
public void shoot() {
System.out.println("탕탕");
}
@Override
public void load() {
System.out.println("재장전");
}
}
public interface Tank {
public void launch();
public void reload();
}
class k9 implements Tank {
@Override
public void launch() {
System.out.println("쾅");
}
@Override
public void reload() {
System.out.println("재장전!");
}
}
public class Adapter implements Gun{
private final Tank tank;
public Adapter(Tank tank) {
this.tank = tank;
}
@Override
public void shoot() {
tank.launch();
}
@Override
public void load() {
tank.reload();
}
}
Adapter가 Tank 클래스를 생성자의 인자로 받아서 초기화하고 있다. 그리고 Gun 인터페이스를 구현해서(내부적으로 Tank 인터페이스를 사용) 기존 코드와의 호환성을 제공하고 있다.
어댑터 패턴을 사용하면 기존의 코드를 건드리지 않고 안전하고 손쉽게 새로운 기능을 확장할 수 있을 것이다. 또한 호환되지 않는 코드에 다리 역할을 할 수도 있을 것이다. 다만, 어댑터 패턴을 사용하다 보면 클래스가 많아져서 코드의 복잡도가 증가할 수도 있을 것이다. 상황에 맞게 적절하게 사용하려는 자세가 필요할 것 같다.