[디자인패턴 수업 12주차 1차시] Adapter Pattern - 정의와 "Duck/Turkey" 예시

Jin Hur·2021년 11월 16일
0
post-custom-banner

객체지향 어댑터도 결국 일상 생활에서 쓰이는 어댑터와 똑같은 역할을 한다. 어떤 인터페이스를 클라이언트에서 요구하는 형태의 인터페이스에 적응시켜주는 역할을 한다.

어댑터는 클라이언트로부터 요청을 받아서 새로운 업체에서 제공하는 클래스(vendor class)에서 받아들일 수 있는 형태의 요청으로 변환시켜주는 중개인 역할을 한다.
기존 시스템과 업체에서 제공한 클래스의 코드에는 변화가 없고, 새 코드에 해당하는 어댑터만 추가하면 된다.


Duck, Turkey 인터페이스와 TurkeyAdapter 클래스

public interface Duck {
    public void quack();
    public void fly();
}
public interface Turkey {
    public void gobble();   // 칠면조는 꽥꽥거리지 않고 골골거린다.
    public void fly();      // 칠면조도 날 순 있지만, 별로 멀리 날 수 없다.
}
public class MallardDuck implements Duck{
    @Override
    public void quack() {
        System.out.println("Quack");
    }

    @Override
    public void fly() {
        System.out.println("I'm flying");
    }
}
public class WildTurkey implements Turkey{
    @Override
    public void gobble() {
        System.out.println("gobble gobble");
    }

    @Override
    public void fly() {
        System.out.println("I'm flying a short distance");
    }
}

어탭터 클래스

// Duck 객체가 모자라서 Turkey 객체를 대신 사용해야 하는 상황.
// 인터페이스가 다르기에 어댑터 클래스를 만들어야 함.
public class TurkeyAdapter implements Duck{
    // 원래 형식의 객체에 대한 레퍼런스가 필요.
    // 생성자에서 레퍼런스를 받아오는 작업을 함.
    Turkey turkey;
    public TurkeyAdapter(Turkey turkey){
        this.turkey = turkey;
    }

    @Override
    public void quack() {
        turkey.gobble();
    }
    // 두 인터페이스 모두 fly()가 들어있지만 칠면조는 오리처럼 멀리 날지 못하다.
    // Turkey의 fly() 메서드를 Duck의 fly() 메서드에 대응시키기 위해 5번 호출하여 더 멀리 날아가도록 함.
    @Override
    public void fly() {
        for(int i=0; i<5; i++){
            turkey.fly();
        }
    }
}

Main

public class MainTest {
    public static void main(String[] args) {
        MallardDuck duck = new MallardDuck();
        WildTurkey turkey = new WildTurkey();

        // 어댑터 적용
        // 클라이언트에서 요구하는 인터페이스는 오직 Duck 형식이다. 
        // 이 Duck 형식의 인터페이스에 Turkey가 맞추기 위해 어뎁터를 사용한다. 
        Duck turkeyAdapter = new TurkeyAdapter(turkey);

        System.out.println("turkey says..");
        turkey.gobble();
        turkey.fly();

        System.out.println();

        System.out.println("TurkeyAdapter says..");
        turkeyAdapter.quack();
        turkeyAdapter.fly();
    }
}


클라이언트에서 어댑터를 사용하는 방법

  1. 클라이언트에서 타겟 인터페이스를 사용하여 메서드 호출함으로써 어댑터에 요청을 함.
  2. 어댑터에서는 어댑티 인터페이스를 사용하여 그 요청을 어댑티에 대한 (하나 이상의) 메서드 호출을 변환.
  3. 클라이언트에서는 호출 결과를 받긴 하지만 중간에 어댑터가 껴 있는지는 전혀 알지 못함.


정의

어댑터 패턴: 한 클래스의 인터페이스를 클라이언트에서 사용하고자 하는 다른 인터페이스로 변환. 어댑터를 이용하면 인터페이스 호환성 문제 때문에 같이 쓸 수 없는 클래스들을 연결해서 쓸 수 있다.

어댑터 패턴에는 여러 객체지향 원칙이 반영됨. 어댑티를 새로 바뀐 인터페이스로 감쌀 때는 '객체 구성'을 사용. 이런 접근법을 쓰면 어댑티의 어떤 서브클래스에 대해서도 어댑터를 쓸 수 있다는 장점이 있다.

그리고 이 패턴에서느 클라이언트를 특정 구현이 아닌 인터페이스에 연결시킨다. 각각 서로 다른 백엔드 클래스들로 변환시키는 여러 어댑터를 사용할수도 있다. 이렇게 인터페이스를 기준으로 코딩을 했기에 타겟 인터페이스만 제대로 지킨다면 나중에 다른 구현을 추가하는 것도 가능하다.

추가 학습 사항으로 (1) 클래스 어댑터(다중 상속 가능할 때)가 있다. 앞서 학습한 내용들은 객체 어댑터 방식에 속한다.
(2) p286 어댑터 실전 예제

post-custom-banner

0개의 댓글