객체지향 및 기반 언어/디자인 패턴

[Design Pattern] Chapter 07 어댑터 패턴과 퍼사드 패턴

박지환 2022. 10. 8. 20:28

어댑터 패턴의 정의

어댑터 패턴(Adapter Pattern)은 특정 클래스 인터페이스를 클라이언트에서 요구하는 다른 인터페이스로 변환함. 인터페이스가 호환되지 않아 같이 쓸 수 없었던 클래스를 사용할 수 있게 도와준다.

어댑터 패턴 UML

 

어댑터에서 타깃 인터페이스를 구현하며, 클라이언트에서 요청을 보내면 어댑터를 통해 최종적으로 어댑티에게 위임된다.

어댑터 패턴 예제 코드

public interface Duck {

    public void quack();
    public void fly();
}
public interface Turkey {

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

Duck 인터페이스와 Turkey 인터페이스는 서로 다른 메소드를 가지고 있다.

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

TurkeyAdapter 어댑터 클래스는 Duck을 구현하며, Duck의 메소드 안에서 Turkey의 메소드를 호출하는 방식으로 행동을 위임한다.

public class DuckTestDrive {

    public static void main(String[] args) {
        Duck duck = new MallardDuck();

        Turkey turkey = new WildTurkey();
        Duck turkeyAdapter = new TurkeyAdapter(turkey);

        System.out.println("칠면조가 말하길");
        turkey.gobble();
        turkey.fly();;

        System.out.println("\n오리가 말하길");
        testDuck(duck);

        System.out.println("\n칠면조 어뎁터가 말하길");
        testDuck(turkeyAdapter);
    }

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

결과적으로 Turkey 클래스를 TurkeyAdapter 어댑터와 연결하면, TurkeyAdapter 클래스를 기존의 동일한 Duck 메소드 호출 코드(quack(), fly())로 Turkey의 메소드(gobble(), fly())도 호출할 수 있게 된다.

클래스 어댑터

클래스 어댑터는 타킷와 어댑티 모두 서브클래스로 만들어서 사용한다. 다만 자바에서는 다중 상속이 불가능하므로 사용할 수 없다.

실전 적용! 어댑터 패턴

자바의 예전 컬렉션 순회 방법이였던 Enumeration과, 현재의 Iterator를 어댑터 패턴을 통해 Iterator 만으로도 Enumeration을 사용하는 구형 코드와, Iterator를 사용하는 현재 코드를 모두 지원할 수 있도록 할 수 있다.

어댑터 디자인하기

Iterator (타깃 인터페이스) → EnumerationIterator (어댑터) → Enumeration (어댑티)

다만 Iterator에는 존재하지만 EnumerationIterator에는 존재하지 않는 remove() 메소드는 UnsupportedOperationException을 통해 예외처리해야 한다.

퍼사드 패턴의 정의

퍼사드 패턴(Facade Pattern)은 서브시스템에 있는 일련의 인터페이스를 통합 인터페이스로 묶어 준다. 또한 고수준 인터페이스도 정의하므로 서브시스템을 더 편리하게 사용할 수 있다.

퍼사드 패턴 + (최소 지식 원칙) UML

단순화된 통합 인터페이스를 통해 클라이언트가 서브시스템을 더 편리하게 사용할 수 있다.

퍼사드 패턴 코드 예시

public class HomeTheaterFacade {
    Amplifier amp;
    Tuner tuner;
    StreamingPlayer player;
    Projector projector;
    TheaterLights lights;
    Screen screen;
    PopcornPopper popper;

    public HomeTheaterFacade(Amplifier amp, Tuner tuner, StreamingPlayer player, Projector projector, TheaterLights lights, Screen screen, PopcornPopper popper) {
        this.amp = amp;
        this.tuner = tuner;
        this.player = player;
        this.projector = projector;
        this.lights = lights;
        this.screen = screen;
        this.popper = popper;
    }

    public void watchMovie(String movie) {
        System.out.println("영화 볼 준비 중");
        popper.on();
        popper.pop();
        lights.dim(10);
        screen.down();
        projector.on();
        projector.wideScreenMode();
        amp.on();
        amp.setStreamingPlayer(player);
        amp.setSurroundSound();
        player.on();
        player.play(movie);
    }

    public void endMovie() {
        System.out.println("홈시어터를 끄는 중");
        popper.off();
        lights.on();
        screen.up();
        projector.off();
        amp.off();
        player.stop();
        player.off();
    }
}

HomeTheaterFacade 퍼사드 클래스는 하위 시스템들의 클래스들의 메소드(인터페이스)들을 통합된 메소드(인터페이스)로 묶고 있다.

public class HomeTheaterTestDrive {

    public static void main(String[] args) {
        Amplifier amp = new Amplifier();
        Tuner tuner = new Tuner();
        StreamingPlayer player = new StreamingPlayer();
        Projector projector = new Projector();
        TheaterLights lights = new TheaterLights();
        Screen screen = new Screen();
        PopcornPopper popper = new PopcornPopper();

        HomeTheaterFacade homeTheater = new HomeTheaterFacade(amp, tuner, player, projector, lights, screen, popper);

        homeTheater.watchMovie("인디아나 존스:레이더스");
        homeTheater.endMovie();
    }
}

이를 통해 서브 시스템들(Amplifier, StreamingPlayer 등)의 메소드들(amp.on(), player.on() 등)을 하나씩 호출할 필요 없이, 퍼사드 클래스 HomeTheaterFacade의 메소드 (watchMovie())를 통해 서브시스템들이 수행해야 하는 일련의 메소드들을 묶어서 간단히 실행할 수 있다.

최소 지식 원칙

디자인 원칙 7: 최소 지식 원칙(Principle of Least Knowledge) 객체 사이의 상호작용은 그 객체와 상호작용을 하는 클래스에게만 허용하라

친구를 만들지 않고 다른 객체에 영향력 행사하기

최소 지식 원칙을 따르려면 객체가 대신 요청하도록 만들어야 한다.

원칙을 따르지 않은 경우

public float getTemp() {
	Thermometer thermometer = station.getThermometer(); // station으로부터 thermometer 객체를 받은 다음, 그 객체의 getTemperature() 메소드를 직접 호출함.
	return thermometer.getTemperature();
}

원칙을 따르는 경우

public float getTemp() {
	return station.getTemperature(); // 최소 지식 원칙을 적용해서 thermometer에게 요청을 전달하는 메소드를 Station 클래스에 추가하였음 -> 의존해야 하는 클래스의 개수를 줄일 수 있음.
}

핵심 정리

  • 기존 클래스를 사용하려고 하는데 인터페이스가 맞지 않으면 어댑터를 쓰면 됨.
  • 큰 인터페이스와 여러 인터페이스를 단순하게 바꾸거나 통합해야 하면 퍼사드를 쓰면 됨.
  • 어댑터는 인터페이스를 클라이언트에서 원하는 인터페이스로 바꾸는 역할을 함.
  • 퍼사드는 클라이언트를 복잡한 서브시스템과 분리하는 역할을 함.
  • 어댑터를 구현할 때는 타깃 인터페이스의 크기와 구조에 따라 코딩해야 할 분량이 결정됨.
  • 퍼사드 패턴에서는 서브시스템으로 퍼사드를 만들고 진짜 작업은 서브클래스에 맡김.
  • 어댑터 패턴에는 객체 어댑터 패턴과 클래스 어댑터 패턴이 있음. 클래스 어댑터를 쓰려면 다중 상속이 가능해야 함.
  • 한 서브시스템에 퍼사드를 여러개 만들어도 됨.
  • 어댑터는 객체를 감싸서 인터페이스를 바꾸는 용도로, 데코레이터는 객체를 감싸서 새로운 행동을 추가하는 용도로, 퍼사드는 일련의 객체를 감싸서 단순하게 만드는 용도로 쓰임.

 

GitHub Code Reference: https://github.com/Krapi0314/Design_Patterns

 

GitHub - Krapi0314/Design_Patterns: Java Implementation of GoF's Design Patterns

Java Implementation of GoF's Design Patterns. Contribute to Krapi0314/Design_Patterns development by creating an account on GitHub.

github.com

Reference: http://www.yes24.com/Product/Goods/108192370

 

헤드 퍼스트 디자인 패턴 - YES24

유지관리가 편리한 객체지향 소프트웨어 만들기!“『헤드 퍼스트 디자인 패턴(개정판)』 한 권이면 충분하다.이유 1. 흥미로운 이야기와 재치 넘치는 구성이 담긴 〈헤드 퍼스트〉 시리즈! 하나

www.yes24.com