본문 바로가기

OOP, FP/디자인패턴

헤드퍼스트 디자인 패턴: 8. 퍼사드 패턴


지난 포스팅에선 호환되지 않는 인터페이스를 변환하여 클라이언트에게 제공하는 어댑터 패턴에 대해 알아보았다.


이번엔 서브시스템의 일련의 인터페이스에 대한 통합된 인터페이스를 제공하는 퍼사드 패턴에 대해 복습한다.


퍼사드 패턴

어떤 서브시스템의 일련의 인터페이스에 대한 통합된 인터페이스를 제공합니다.

퍼사드에서 고수준 인터페이스를 정의하기 때문에 서브시스템을 더 쉽게 사용할 수 있습니다.


퍼사드 패턴은 단순화된 인터페이스를 통해서 서브시스템을 더 쉽게 사용할수 있도록 하기 위한 용도로 사용된다.


어떤 서브시스템에 속한 일련의 복잡한 클래스들을 단순화하고 통합한 클래스를 만들어서 제공한다.

다른 패턴과 달리 퍼사드 패턴은 상당히 단순하며 복잡한 추상화와같은 개념이 필요 없다.


퍼사드 패턴을 이용하면 클라이언트와 서브시스템이 서로 긴밀하게 연결되지 않아도 된다.


책에 등장한 예제를 살펴보자.


집에서 영화를 보기위한 홈 씨어터 시스템을 구축한다.

DVD 플레이어, 프로젝터, 자동 스크린, 서라운드 음향 및 팝콘 기계를 갖춘 시스템이다.





전선과 프로젝터를 설치하고 각 장치들을 케이블로 연결하고 자잘한 부분을 조절하고 나면

영화를 보기 위한 준비를 마쳤다고 할 수 있다.


이제 영화를 보기 위한 일련의 행동을 살펴보자.


(1) 팝콘 기계를 켠다 - PopcornPopper.on()

(2) 팝콘 튀기기 시작 - PopcornPopper.pop()

(3) 전등을 어둡게 조절 - TheaterLights.dim()

(4) 스크린을 내린다 - Screen.down()

(5) 프로젝터를 켠다 - Projector.on()

(6) 프로젝터로 DVD신호가 입력되도록 한다 - Projector.setInput()

(7) 프로젝터를 와이드 스크린 모드로 전환한다 - Projector.wideScreenMode()

(8) 앰프를 켠다 - Amplifier.on()

(9) 앰프 입력을 DVD로 전환한다 - Amplifier.setDvd()

(10) 앰프를 서라운드 음향 모드로 전환한다 - Amplifier.setSurroundSound()

(11) 앰프 볼륨을 설정한다 - Amplifier.setVolumn()
(12) DVD 플레이어를 켠다 - DvdPlayer.on()

(13) DVD를 재생한다 -DvdPlayer.play()


후 힘들다. 콘 기계, 전등, 스크린, 프로젝터, 앰프와 DVD 총 6개의 클래스가 필요하다.

메소드 호출은 총 13번이나 필요하다.


영화를 다 시청하고 나면 다시 위의 순서를 역으로 반복해야 한다.

CD나 라디오를 들을 때는 순서가 다르거나 작동 방법이 다를것 이다.

이 홈 씨어터 시스템을 업그레이드하려면 또 다른 작동 방법이 추가되고.... 사용자는 이 작동 방법을 다시 배워야한다.


이럴 때 퍼사드 패턴을 이용하면 복잡한 일을 간단하게 처리하고 영화를 즐길 수 있게 된다!

복잡한 시스템을 직접 건드리고 싶다면 위의 시스템을 그대로 쓰면 된다. 하지만 간단하고 편리한 것을 원한다면

퍼사드 패턴이 우리에게 도움을 줄 수 있다.



복잡해보이지만 기존 홈 씨어터 시스템에서 새로운 클래스(HomeTheaterFacade)가 하나 추가되었을 뿐이다.

홈 씨어터 시스템용 퍼사드 클래스는 watchMovie()와 같이 몇 가지 간단한 메소드만 들어있다.

퍼사드 클래스에서는 홈 씨어터 구성요소들을 하나의 서브시스템으로 간주하고,

watchMovie()메소드에서는 서브시스템의 메소드들을 호출하여 필요한 작업을 처리한다.


이제 홈 씨어터 시스템을 사용하려는 클라이언트에서는 각각의 서브시스템들이 아닌 퍼사드 클래스의 메소드를 호출하게 된다.

watchMovie()메소드만 호출하면 알아서 앞의 6개의 서브시스템을 이용해서 13번의 메소드 호출을 처리해줄 것이다.


기존 서브시스템은 어느 부분도 바뀌지 않았으며 여전히 직접 접근 또한 가능하다.

서브시스템 클래스의 고급 기능이 필요하다면 언제든 마음대로 쓸 수 있다.


홈씨어터퍼사드 클래스의 코드 예시를 보면 좀 더 이해가 쉽다.

public class HomeTheaterFacade {

// 서브 시스템

Amplifier amp;

Tuner tuner;

DvdPlayer dvd;

CdPlayer cd;

Projector projector;

TheaterLights lights;

Screen screen;

PopcornPopper popper;


public HomeTheaterFacade(.........) { // 인스턴스 변수 초기화 };


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.setDvd(dvd);

amp.setSurroundSound();

amp.setVolumn(5);

dvd.on();

dvd.play(movie);

}

}


watchMovie()메소드를 이용해 앞에서 일일이 순서대로 했던 13개의 작업을 처리한다.

꽤 복잡한 모든 일들을 하나의 메소드로 간단하게 처리할 수 있는 기능을 제공하는 것이다.

구현 방법이 복잡할 것도 없는 것이, 각 작업은 서브시스템에 들어있는 구성요소들에게 위임하기만 하면 된다.



최소 지식 원칙

퍼사드 패턴은 최소 지식 원칙(Principle of Least knowledge)을 지키는데 많은 도움이 된다.

최소 지식 원칙은 객체 사이의 상호작용은 될 수 있으면 아주 가까운 "친구"사이에서만 허용하라는 원칙이다.


시스템을 디자인할 때, 어떤 객체든 그 객체와 상호작용을 하는 클래스의 개수에 주의해야 하며

그런 객체들이 어떤 식으로 상호작용을 하는지에도 주의를 기울여야 한다.


이 원칙을 준수하게 되면 여러 클래스들이 복잡하게 얽혀서 시스템의 한 부분을 변경했을 때

다른 부분까지 줄줄이 고쳐야 하는 사황을 미치 방지할 수 있다.


자세한 내용은 헤드퍼스트 디자인 패턴: 디자인 원칙 7~9을 참조하자.


의문점

(1) 퍼사드에서 기능을 추가하거나 각각의 요청을 서브시스템에 그대로 전달하기도 하는가?

- 퍼사드에서 서브시스템을 단순하게 활용하는것 외에에 자유롭게 기능을 추가할 수 있다.


(2) 한 서브시스템에 퍼사드를 하나씩만 만들어야 하는가?

- 퍼사드 패턴에서 특정 서브시스템에 대해 만들 수 있는 퍼사드의 개수에는 제한이 없다.


(3) 더 간단한 인터페이스를 제공하는것 외에 장점은?

- 클라이언트의 구현과 서브시스템을 분리시킬 수 있다.

  위의 홈 씨어터 시스템을 업그레이드 한다고 생각해보자

  만약 클라이언트가 퍼사드를 가지고 만들어 놨다면 클라이언트의 코드를 고치지 않고

  퍼사드만 바꾸면 될 것이다.


(4) 어댑터 패턴과 퍼사드 패턴의 차이점은?

- 어댑터 패턴에서는 하나 이상의 클래스의 인터페이스를 클라이언트에서 필요로 하는 인터페이스로 변환한다.

  클라이언트에서 코딩할 때 썻던 여러 인터페이스에 맞추기 위해 여러 클래스를 감싸야 할 수도 있다.

  퍼사드 패턴에서도 여러가지 클래스를 감싸야만 하는 것은 아니다. 아주 복잡한 인터페이스를 가지고 있는

  단 한 개의 클래스에 대해서 퍼사드를 만들 수도 있다.

  이처럼 어댑터와 퍼사드의 차이점은 감싸는 클래수의 개수에 있는 것이 아니라 그 용도에 있다.

  어댑터 패턴은 인터페이스 호환성, 퍼사드 패턴은 서브시스템에 대한 간단한 인터페이스 제공을 위한 용도로 사용된다.



서브시스템의 일련의 인터페이스에 대한 통합된 인터페이스를 제공하는 퍼사드 패턴에 대한 복습을 마치겠다.



1. 스트래티지 패턴

2. 옵저버 패턴

3. 데코레이터 패턴

4-1. 팩토리 메서드 패턴

4-2. 추상 팩토리 패턴

5. 싱글턴 패턴

6. 커맨드 패턴

7. 어댑터 패턴

8. 퍼사드 패턴