본문 바로가기
공부/디자인 패턴

7-1. Adapter Pattern (Head First Design Pattern)

by 김주현3902 2024. 10. 25.

다른 목적으로 일부 객체를 감쌀 것이다.

인터페이스가 실제와 다르게 보이도록 만들기 위해서이다.
원하는 설계의 인터페이스에 다른 인터페이스를 구현하는 클래스를 적용시킬 수 있다.

 

규격이 다른 전자제품을 외국에서 쓸 때, 규격에 맞는 어댑터가 있으면 사용할 수 있다.
마찬가지로, 내가 가진 시스템에 규격이 다른 클래스를 추가하고 싶을 때, 어댑터를 이용하여 해결할 수 있다.

 

어댑터 패턴은 클래스의 인터페이스를 클라이언트가 기대하는 다른 인터페이스로 변환한다.
어댑터는 호환되지 않는 인터페이스 때문에 함께 작업할 수 없었던 클래스들이 함께 동작할 수 있게 한다.

 

quack과 fly 메소드를 가진 Duck 인터페이스와, gobble과 fly 메소드를 가진 Turkey 인터페이스가 있다.
Turkey 인터페이스를 참조한 클래스가 Duck을 참조하기 위해서 TurkeyAdapter 클래스를 생성한다.
TurkeyAdapter는 turkey 변수를 가지고 있으며, 생성자에서 이를 주입받는다.
Duck 인터페이스가 가진 quack 메소드에서는, turkey의 gobble 메소드가 실행되도록 하고,
fly 메소드 또한 turkey에 해당하는 fly를 실행하도록 한다.
위 과정을 거치면 TurkeyAdapter는 Duck으로서 quack과 fly 메소드를 실행할 수 있다.

public interface Duck {
    public void quack();
    public void fly();
}
public class MallardDuck implements Duck {

    @Override
    public void quack() {
        System.out.println("Mallard Duck Quack");
    }

    @Override
    public void fly() {
        System.out.println("Mallard Duck Fly");
    }
}
public interface Turkey {
    public void gobble();
    public void fly();
}
public class WildTurkey implements Turkey {

    @Override
    public void gobble() {
        System.out.println("Wild Turkey Gobble");
    }

    @Override
    public void fly() {
        System.out.println("Wild Turkey Fly");
    }
}
public class TurkeyAdapter implements Duck {

    Turkey turkey;

    public TurkeyAdapter(Turkey turkey) {
        this.turkey = turkey;
    }

    @Override
    public void quack() {
        turkey.gobble();
    }

    @Override
    public void fly() {
        for (int i = 0; i < 5; i++) {
            turkey.fly();
        }
    }
}
public class AdapterSimulation {
    public static void main(String[] args) {
        Duck mallardDuck = new MallardDuck();
        Turkey wildTurkey = new WildTurkey();
        Duck turkeyAdapter = new TurkeyAdapter(wildTurkey);

        testDuck(mallardDuck);
        testDuck(turkeyAdapter);
    }
    static void testDuck(Duck duck) {
        duck.quack();
        duck.fly();
    }
}

turkeyAdapter가 Duck으로서 잘 작동하는 것을 확인할 수 있다.

 

 

Adapter Pattern에는 두 종류가 있다. -> object adapter, class adapter

위 TurkeyAdapter는 adapter pattern의 예시이다.

 

class adapter는 기존 클래스와 추가 클래스를 동시에 상속받아 문제를 해결한다.
따라서, class adapter는 다중 상속이 필요하다. 이 기능은 자바에서는 불가능하다.
(하지만 인터페이스의 다중 구현은 가능하기에 인터페이스로 만들면 구현 가능하다)

 

adapter pattern의 실제 예시로 Enumerators와 Iterators가 있다.
Enumerators는 Vector, Stack 등의 여러 컬렉션이 구현하며, hasMoreElements()와 nextElement() 메소드를 가진다.
최근에는 Enumerators 대신 Iterators를 사용한다.
Iterators는 hasNext(), next(), remove() 메소드를 가진다.
Enumerators를 Iterators 처럼 사용하기 위해 adapter patern을 사용한다.
EnumerationIterator는 Enumerators를 가지며, 이를 생성자로 주입받고, Iterator를 구현한다.
이 때, hasNext()는 hasMoreElements()로, next()는 nextElement()로 구현한다.
다만, Enumerator에는 remove 기능이 없기에, 이는 지원하지 않는다.