본문 바로가기
  • Where there is a will there is a way.
개발/java

어댑터 패턴

by 소확행개발자 2019. 7. 1.

어댑터 패턴

객체지향 어댑터가 뭔지는 그리 어렵지 않게 이해할 수 있을 것이다.

 

흔히 미국여행 갔을 경우 어댑터가 달라서 준비해간 경험이 있다면 말이다.

 

어댑터의 역할은 국산 플러그와 미국식 소켓 사이에서 국산 전원 플러그를 미국식 소켓에 꽂을 수 있게 해주는 역할을 한다. 

 

그렇다면 객체지향 어댑터는 어떨까?

 

객체지향 어댑터

 

어떤 소프트웨어 시스템이 있는데, 새로운 업체에서 제공한 클래스 라이브러리를 사용해야 한다고 해 보자. 그런데 새로 채택한 업체에서 사용하는 인터페이스가 기존 업체에서 사용하던 인터페이스하고 다르다고 가정해 보자.

 

하지만 기존의 코드를 바꿔서 이 문제를 해결할 수는 없는 상황이다.

 

그렇다고 업체에서 공급 받은 클래스도 변경할 수 없고..

 

그렇다면 이때 한 업체에서 사용하는 인터페이스를 기존에 사용하던 인터페이스에 적응시켜주는 클래스를 만들 수 있을 것이다.

 

 

즉 어댑터는 클라이언트로부터 요청을 받아서 새로운 업체에서 제공하는 클래스에서 받아들일 수 있는 형태의 요청으로 변환시켜주는 중개인 역할을 한다.

 

package adapter;

public interface Duck {

    public void quack();
    public void fly();

}

 

package adapter;

public class MallardDuck implements Duck {


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


    @Override
    public void fly() {
        System.out.printf("i'm flying");
    }
}

 

package adapter;

public interface Turkey {

    public void gobble();
    public void fly();
}

 

package adapter;

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");
    }
}

 

package adapter;

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();
        }
    }
}

 

import adapter.Duck;
import adapter.TurkeyAdapter;
import adapter.WildTurkey;

public class Main {

    public static void main(String[] args) {

        WildTurkey turkey = new WildTurkey();
        Duck turkeyAdapter = new TurkeyAdapter(turkey);
        turkeyAdapter.quack();
        turkeyAdapter.fly();


    }
}

 

1. 클라이언트에서 타겟 인터페이스를 사용하여 메소드를 호출합니다.

2. 어댑처에서는 어댑티 인터페이스를 사용하여 그 요청을 메소드 호출로 변환합니다.

3. 클라이언트에서는 호출 결과를 받긴 하지만 중간에 어댑터가 껴 있는지는 전혀 알지 못합니다.

 

특정 클라이언트를 특정 구현이 아닌 인터페이스에 연결시키면 인터페이스 기준으로 코딩을 했기 때문에 타겟 인터페이스만 제대로 지킨다면 나중에 다른 구현을 추가하는 것도 가능하다.

 

 

---

 

package main.adapter.service;


/**
 * 상품의 배너를 만들어주는 클래스
 *
 */
public class Banner {

    private String string;

    public Banner(String string){
        this.string = string;
    }

    public void showWithSmall(){

        System.out.println("(" + string + ")");

    }

    public void showWithLarge(){

        System.out.println("{" + string + "}");

    }

}
package main.adapter.service;

public interface Printable {

    void printWeak();
    void printStrong();

}
package main.adapter.service;

public class PrintableBannerAdapter implements Printable {

    private Banner banner;

    PrintableBannerAdapter(String string){
        this.banner = new Banner(string);
    }


    @Override
    public void printWeak() {
        banner.showWithSmall();

    }


    @Override
    public void printStrong() {
        banner.showWithLarge();
    }
}

 

 

 

만약 기존에 사용하던 Banner 가 버그도 없고 최대한 재이용하고 싶은 경우가 있다면 ?

 

새로 만드는 클래스에서 어댑터를 만든 뒤에 부품으로 재이용하면 된다.

 

package main.adapter.service;

import static org.junit.Assert.*;
import org.junit.Test;


/**
 * 어뎁터를 맞춰 낄 수 있다.
 *
 * 다른 어뎁터가 있다면 다른 어뎁터로 바꿔끼면 된다.
 */
public class PrintableBannerAdapterTest {


    @Test
    public void printWeak() {

        Printable p = new PrintableBannerAdapter("test");

        p.printWeak();

    }


    @Test
    public void printStrong() {

        Printable p = new PrintableBannerAdapter("test2");

        p.printStrong();
    }
}

 

'개발 > java' 카테고리의 다른 글

java 복잡한 Comparator 예제  (0) 2019.12.19
템플릿 메소드 패턴  (2) 2019.07.04
커맨드 패턴  (0) 2019.06.30
자바 날짜계산 유틸리티 구현  (0) 2019.02.19
java Ramda 리스트 컬랙션 데이터 예제  (0) 2019.01.30

댓글