본문 바로가기

ComputerScience/DesignPattern

[DesignPattern] 퍼사드 패턴(Facade Pattern)

퍼사드 패턴(Facade Pattern)

퍼사드 패턴은 서브 시스템에 있는 일련의 인터페이스를 통합 인터페이스로 묶어줍니다.

또한 고수준 인터페이스도 정의하므로 서브시스템을 더 편하게 쓸 수 있습니다.

 

이미지 출처: https://en.wikipedia.org/wiki/Facade_pattern

위의 UML Class Diagram에서, Client 클래스는 Subsystem 클래스들에 직접적으로 접근하지 않습니다.

대신, Client는 간단한 인터페이스를 구현하고 Subsystem 클래스들 (Class1, Class2 및 Class3)을 통해 작동하는 Facade 클래스를 통해 작업합니다. Client는 단순한 Facade 인터페이스에만 의존하며 복잡한 서브시스템에 대해 독립적입니다.

여기서 중요한 포인트는 Facade 디자인 패턴을 사용하여 복잡한 서브시스템을 단순한 인터페이스로 감싸고, 클라이언트가 이 간소화된 인터페이스를 통해 상호작용할 수 있도록 하는 것입니다.

클라이언트는 직접적으로 서브시스템 클래스들과 상호작용할 필요 없이 "Facade" 클래스를 통해 간접적으로 서브시스템과 소통할 수 있습니다.

이는 시스템을 모듈화 하고, 클라이언트와 서브시스템 간의 결합도를 낮추어 시스템을 보다 유연하고 유지보수하기 쉽게 만드는 디자인 패턴 중 하나입니다.

 

예를 들어 봅시다.

 

영화를 보기위해 홈 시어터를 구축하고 싶습니다.

이미지 출처: https://github.com/IT-Book-Organization/HeadFirst-DesignPattern/blob/main/Chapter_07/README.md

홈 시어터를 위해 여러 클래스가 필요하고, 이 클래스들이 서로 복잡하게 얽혀있다면 많은 인터페이스를 사용해야 할 것입니다.
클라이언트에서 영화를 보는 것을 구현한다고 가정해 보면, 클래스들을 전부 구성하고 하나하나 켜주어야만 합니다.
또, 추가적으로 라디오를 듣는다거나 영화가 끝난다거나 하는 일들이 추가되면 클라이언트의 코드는 매우 복잡해집니다.

 

이미지 출처: https://github.com/IT-Book-Organization/HeadFirst-DesignPattern/blob/main/Chapter_07/README.md

이를 퍼사드 객체를 사용하여 클라이언트와 클래스들의 긴밀한 연결을 끊고, 클라이언트를 대신하여 복잡한 로직들을 구현할 수 있습니다.

 

Facade HomeTheaterFacade

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

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

        this.amp = amp;
        this.tuner = tuner;
        this.player = player;
        this.projector = projector;
        this.screen = screen;
        this.lights = lights;
        this.popper = popper;
    }

    public void watchMovie(String movie) {
        System.out.println("Get ready to watch a movie...");
        popper.on();
        popper.pop();
        lights.dim(10);
        screen.down();
        projector.on();
        projector.wideScreenMode();
        amp.on();
        amp.setStreamingPlayer(player);
        amp.setSurroundSound();
        amp.setVolume(5);
        player.on();
        player.play(movie);
    }


    public void endMovie() {
        System.out.println("Shutting movie theater down...");
        popper.off();
        lights.on();
        screen.up();
        projector.off();
        amp.off();
        player.stop();
        player.off();
    }

    public void listenToRadio(double frequency) {
        System.out.println("Tuning in the airwaves...");
        tuner.on();
        tuner.setFrequency(frequency);
        amp.on();
        amp.setVolume(5);
        amp.setTuner(tuner);
    }

    public void endRadio() {
        System.out.println("Shutting down the tuner...");
        tuner.off();
        amp.off();
    }
}

클라이언트는 퍼사드의 watchMovie(), endMoive() 등을 호출하여 똑같은 기능을 사용할 수 있습니다.
이를 통해 클라이언트는 서브 시스템과 긴밀한 연결을 끊고 분리될 수 있습니다.

디자인 원칙: 최소 지식 원칙

  • 진짜 절친에게만 이야기해야 한다.

위 말은 시스템을 디자인할 때 어떤 객체든 그 객체와 상호작용을 하는 클래스의 개수와 상호작용 방식에 주의를 기울여야 한다는 뜻입니다.
최소 지식 원칙은 소프트웨어 모듈 사이의 결합도를 줄여서 코드의 품질을 높이는 것이 목표입니다.

 

이 원칙을 지키면, 여러 클래스가 복잡하게 얽혀있어 시스템의 한 부분을 변경했을 때 다른 부분까지 줄줄이 고쳐야하는 상황을 미리 방지할 수 있습니다.

 

퍼사드 클래스는 최소 지식 원칙을 다음과 같이 지켰습니다.

  • 퍼사드 클래스는 클라이언트에게 단순하고 일관된 인터페이스를 제공합니다. 클라이언트는 퍼사드 클래스의 메서드만 호출하면 되며, 내부적으로 어떤 서브시스템이 있는지 알 필요가 없습니다. 
  • 퍼사드 클래스는 서브시스템과의 상호작용을 캡슐화하고, 클라이언트는 퍼사드를 통해서만 서브시스템과 소통합니다. 클라이언트는 서브시스템의 존재를 알 필요가 없고, 서브시스템의 변경이나 추가에 영향을 받지 않습니다.
  • 클라이언트는 퍼사드를 통해 간접적으로 서브시스템과 상호작용합니다. 클라이언트는 퍼사드를 통해서만 필요한 기능을 수행하고, 서브시스템에 대한 직접적인 접근이 없습니다.

퍼사드 패턴을 사용하면 클라이언트는 더 적은 수의 클래스와만 상호작용하게 되므로 최소 지식 원칙을 적용하기 용이해집니다.

예를 들어, 아래 코드는 Station 이라는 인스턴스로부터 return 받은 객체의 메소드를 호출하여 최소 지식 원칙을 지키지 못하였습니다.

public float getTemp(){
        Thermometer thermometer = station.getThermometer();
        return thermometer.getTemperature();
}

 

 

이 문제를 해결하려면 Station 객체에 getTemperature() 메소드를 생성하는 것이 옳습니다.

왜냐하면 Temprature를 알려면 이 메소드는 Station을 알아야하고 Thermometer도 알아야 하기 때문입니다.

참고 자료

https://github.com/IT-Book-Organization/HeadFirst-DesignPattern/blob/main/Chapter_07/README.md

https://en.wikipedia.org/wiki/Facade_pattern

 

Facade pattern - Wikipedia

From Wikipedia, the free encyclopedia Software design pattern The facade pattern (also spelled façade) is a software-design pattern commonly used in object-oriented programming. Analogous to a facade in architecture, a facade is an object that serves as a

en.wikipedia.org