.
본문 바로가기
디자인패턴

퍼사드 패턴(Facade Pattern)

by 와칸다개발자 2022. 2. 4.

headfirst 디자인 패턴책을 공부하고 정리한 것 입니다.

 

개요

 

당연하게도 이 패턴을 왜 사용하는지에 대해 아는 것이 정말 중요하다고 볼 수 있습니다.

퍼사드 패턴 정의는 다음과 같습니다. 

퍼사드 패턴은 서브시스템들의 통합된 인터페이스를 제공한다.

 

간단하게 말해서 퍼사드 패턴은 단순화된 인터페이스를 통해 서브시스템을 더 쉽게 사용할 수 있도록 하기 위한 용도입니다. 말로만 떠들어서는 역시 뭔지 알 수 없습니다. 코드를 봅시다.

 

등운동을 하는 코드를 작성해 보겠습니다. 운동 순서는 다음과 같습니다.

 

1. 스트레칭을 몇분동안 한다.

2. 풀업을 한다.

3. 시티드 로우를 한다.

4. 유산소 달리기를 한다.

 

다음과 같이 작성할 수 있습니다.

this.stretching.stretch(10); // 스트레칭 10분동안 함
this.pullup.weight(); // 풀업 함
this.seatedRow.setWeight(100); // 시티드로우를 100kg의 무게로 설정
this.seatedRow.weight(); // 시티드로우 운동 함
this.running.run(20, 10); // 달리기를 20분동안 10의 속도로 함

 

개발자는 언제나 항상!!! 변화되는 요구 사항에 코드를 얼마나 변경해야 할지를 고민하고 신경써야 합니다.

 

 

그런데 등운동을 하는게 아니라 가슴, 어깨 운동을 한다면 어떻게 작성해야 할까요? 

웨이트가 아니라 축구, 족구 등 다른 운동을 해야 한다면 얼마나 코드를 변경하고 작성해야 할까요??

해답은 퍼사드 패턴입니다.

 

 

해결

 

퍼사드 패턴을 통해 해결해봅시다. 먼저 운동 퍼사드 클래스를 구현해야 합니다.

 

퍼사드 클래스에서는 모든 서브시스템(턱걸이, 로우, 달리기, 스트레칭)등에 접근할 수 있도록 해야 한다.

 

ExerciseFacade.ts

class Pullup {
public weight() {
console.log('풀업 운동한다');
}
}
class SeatedRow {
public setWeight(weight: number) {
console.log(`${weight}kg의 무게로 시티드로우 설정`);
}
public weight() {
console.log('시티드 로우 운동한다');
}
}
class Stretching {
public stretch(time: number) {
console.log(`${time}분 동안 스트레칭 한다`);
}
}
class Running {
public run(time: number, speed: number) {
console.log(`${time}분 동안 ${speed}의 속도로 달리기를 한다`);
}
}
class ExerciseFacade {
private stretching: Stretching
private pullup: Pullup;
private seatedRow: SeatedRow;
private running: Running;
constructor(
stretching: Stretching,
pullup: Pullup,
seatedRow: SeatedRow,
running: Running
) {
this.stretching = stretching;
this.pullup = pullup;
this.seatedRow = seatedRow;
this.running = running;
}
public startExercise() {
this.stretching.stretch(4);
this.pullup.weight();
this.seatedRow.setWeight(100);
this.seatedRow.weight();
this.running.run(20, 10);
}
}
const excercise = new ExerciseFacade(
new Stretching(),
new Pullup(),
new SeatedRow(),
new Running());
excercise.startExercise();

 

 

 

 

퍼사드패턴은 일종의 클라이언트에게 리모컨을 제공한다고 볼 수 있습니다.

 

클라이언트가 리모컨으로 운동하기 버튼을 누르면 서브클래스 시스템들을 의존하지도 않고 

 

서브클래스 시스템들의 복잡한 로직을 간단하게 사용하도록 합니다. 

 

uml로 보면 이해하기 쉽습니다.

 

 

최소 지식 원칙

정말 친한 친구들하고만 얘기하라

 

이게 뭔 소릴까요? 즉 시스템 디자인 할 때 어떠한 객체든 그 객체와 상호작용을 하는 클래스의 개수를 주의해야 하며

 

어떤 식으로 상호작용하는지 주의 해야 합니다. 이 디자인원칙을 잘 준수한다면 변화하는 요구사항에 대해 어떠한 부분

 

을 변경했을 때 줄줄이 소세지처럼 엮여있는 코드 변경상황을 나름 잘 방지할 수 있습니다. 

 

제 생각으로 결합도를 낮추라는 말 같습니다?

 

자 그럼 어찌 해야 객체간 상호작용을 최소화 할 수 있을까요? 여기 네가지 가이드 라인이 있습니다.

 

1. 객체 자체

2. 메소드에 인자로 전달된 객체

3. 메소드에서 생성하거나 인스턴스를 만든 객체

4. 그 객체에 속하는 구성요소

 

예를들어 어떠한 메소드를 호출한 결과로 리턴받은 객체에 메소드를 호출한다면 어떠한 단점이 있을까요??

 

객체의 일부분에 대해서 요청을 하게 되고 결국 상호작용을 하는 객체의 수가 많아 진다.

 

최소 지식원칙을 준수하려면 그 객체 쪽에서 대신 요청을 하도록 만들어야 한다.

 

역시 말로만 떠들면 아무것도 들어오는 것이 없습니다. 코드를 봅시다.

 

원칙을 따르지 않는 경우

class Thermometer {
private readonly temperature: number = 23;
public getTemperature() {
return this.temperature;
}
}
class Station{
private thermometer: Thermometer = new Thermometer();
public getThermometer(){
return this.thermometer;
}
}
class Client{
private station:Station;
constructor(s: Station){
this.station = s;
}
public getTemp1(): number {
const thermometer: Thermometer = this.station.getThermometer();
return thermometer.getTemperature();
}
}
const cli : Client = new Client(new Station());
console.log(cli.getTemp1());

 

client 클래스에 getTemp1() 메소드를 보면 station 객체로부터 온도계 객체를 리턴 받고 리턴받은 온도계 객체의 온도반환 메소드를 직접 호출합니다. 

 

원칙을 따르는 경우

class Thermometer {
private readonly temperature: number = 23;
public getTemperature() {
return this.temperature;
}
}
class Station{
private thermometer: Thermometer = new Thermometer();
public getThermometer(){
return this.thermometer;
}
public getTemperature() {
return this.thermometer.getTemperature();
}
}
class Client{
private station:Station;
constructor(s: Station){
this.station = s;
}
public getTemp1(): number {
const thermometer: Thermometer = this.station.getThermometer();
return thermometer.getTemperature();
}
public getTemp2():number{
return this.station.getTemperature();
}
}
const cli : Client = new Client(new Station());
console.log(cli.getTemp2());

 

getTemp2() 메소드에서 Station 객체에 온도 반환 메소드를 추가하고 작성했습니다.

위 코드는 의존하는 객체의 수가 적습니다. 또 다른 예시를 보겠습니다.

 

위 네가지 가이드라인을 다시 한번 적고 보겠습니다.

 

1. 객체 자체

2. 메소드에 인자로 전달된 객체

3. 메소드에서 생성하거나 인스턴스를 만든 객체

4. 그 객체에 속하는 구성요소

 

class Engine{
public start(){
console.log('엔진 스타트');
}
}
class Key{
public turns(): boolean{
return true;
}
}
class Doors{
public lock(){
console.log('문 잠김');
}
}
class Car{
private engine: Engine;
constructor(e: Engine){
this.engine = e;
}
public start(key: Key){ // 2번 매개변수로 전달된 객체는 호출가능
const doors:Doors = new Doors(); // 3번 메소드 내 생성한 인스턴스 메소드 호출가능
const authorized:boolean = key.turns(); // 2번 매개변수로 전달된 객체는 호출가능
if(authorized === true){
this.engine.start(); // 4번 객체 멤버변수 및 메소드 호출가능
this.updateDashBoardDisplay(); // 4번 객체 멤버변수 및 메소드 호출가능
doors.lock(); // 3번 메소드 내 생성한 인스턴스 메소드 호출가능
}
}
public updateDashBoardDisplay() {
console.log('디스플레이 갱신');
}
}

 

간단정리

 

1. 여러 인터페이스를 단순화 시키거나 통합시켜야 한다면 퍼사드 패턴을 사용한다.

 

2. 퍼사드는 클라이언트와 복잡한 서브클래스 시스템들을 분리시켜 준다.

 

3. 퍼사드는 서브시스템을 가지고 퍼사드를 만들고 실제 작업은 서브클래스에 맡긴다. 

퍼사드는 서브클래스들에 의존하지 않고 복잡한 로직을 몰라도 간편하게 사용할 수 있다.

 

'디자인패턴' 카테고리의 다른 글

팩토리 패턴1 (factory pattern)  (0) 2021.09.24
전략패턴 (Strategy Pattern )  (0) 2021.09.21
데코레이터 패턴(Decorator Pattern)  (0) 2021.09.21
싱글톤 패턴 (singleton pattern)  (0) 2021.09.16

댓글