목차
Adapter Pattern: 다른 인터페이스를 연결하기
어댑터는 기존 객체가 제공하는 인터페이스와 호출자가 기대하는 인터페이스가 다를 때 사이에서 호출을 변환한다. 전기 플러그의 형태를 바꾸는 어댑터에 비유할 수 있지만, 소프트웨어에서는 메서드 이름뿐 아니라 값의 의미와 오류 처리까지 맞춰야 한다. 기존 코드를 직접 수정할 수 없는 외부 라이브러리나 오래된 모듈을 연결할 때 유용하다.
클래스 어댑터: 상속으로 연결
다음 Banner는 괄호나 별표로 문장을 감싼다. 호출자는 Print 인터페이스만 사용하고 싶다고 하자.

class Banner {
private final String text;
Banner(String text) { this.text = text; }
void showWithParen() { System.out.println("(" + text + ")"); }
void showWithAster() { System.out.println("*" + text + "*"); }
}
interface Print {
void printWeak();
void printStrong();
}
final class PrintBanner extends Banner implements Print {
PrintBanner(String text) { super(text); }
public void printWeak() { showWithParen(); }
public void printStrong() { showWithAster(); }
}
new PrintBanner("adapter").printWeak()는 (adapter)를, printStrong()은 *adapter*를 출력한다. PrintBanner가 Banner를 상속해 기존 동작을 재사용하면서 호출자가 요구한 Print 계약을 구현한다. 원본의 printStong 오타는 printStrong으로 고쳤다. 이 방법은 Banner가 상속 가능한 클래스여야 하고, Java에서 이미 다른 클래스를 상속한 어댑터에는 적용할 수 없다.
객체 어댑터: 위임으로 연결

final class DelegatingPrintBanner implements Print {
private final Banner banner;
DelegatingPrintBanner(Banner banner) {
this.banner = java.util.Objects.requireNonNull(banner);
}
public void printWeak() { banner.showWithParen(); }
public void printStrong() { banner.showWithAster(); }
}
new DelegatingPrintBanner(new Banner("adapter"))는 같은 Print 계약을 제공한다. 여기서는 Banner 인스턴스를 합성하고 동작을 위임한다. 기존 클래스가 final이어도 사용할 수 있고, 테스트에서 대체 인스턴스를 주입하기 쉽다. 다만 어댑터가 감싼 객체의 동작 자체를 바꾸는 것은 아니다. 예를 들어 외부 API의 가격 단위가 센트이고 내부 계약이 원이라면 단순 메서드 전달로는 부족하다. 환율이나 단위 변환 규칙을 명시하고, 입력 범위와 오류를 테스트해야 한다.
어댑터는 기존 인터페이스를 사용하는 곳마다 조건문을 넣지 않고 변환 책임을 한곳에 모을 때 이점이 있다. 반대로 두 인터페이스가 원래 같거나 기존 코드를 쉽게 바꿀 수 있다면 별도 래퍼가 불필요할 수 있다.