의도
- 한 서스시스템 내의 인터페이스 집합에 대한 획일화된 하나의 인터페이스를 제공하는 패턴으로, 서브시스템을 사용하기 쉽도록 상위 수준의 인터페이스를 정의함.
언제 쓰는가?
- 복잡한 서브시스템에 대한 단순한 인터페이스 제공이 필요할 때.
- 시스템 범위가 확장되면, 또한 구체적으로 설계되면 서브시스템은 계속 복잡해짐.
- 서브시스템에 대한 단순한 인터페이스를 제공하며 개발자에게 적합한 클래스 형태를 제공함.
- 추상 개념에 대한 구현 클래스와 사용자 사이에 너무 많은 종속성이 존재할 때.
- 퍼사드를 사용하여 사용자와 다른 서브시스템 간 결합도를 줄일 수 있음.
- 서브시스템을 계층화시킬 때.
- 퍼사드 패턴을 사용하여 서브시스템에 대한 접근점을 제공함.
- 서브시스템이 다른 서브시스템에 종속적이라 하더라도, 퍼사드를 통해서만 대화를 진행하게 함으로써 종속성을 줄일 수 있음.
구조
- Facade : 단순하고 일관된 통합 인터페이스를 제공하며, 서브시스템을 구성하는 어떤 클래스가 어떤 요청을 처리해야 하는지 알고 있으며, 사용자의 요청을 해당 서브시스템 객체에 전달함.
- 서브시스템 클래스들 : 서브시스템의 기능을 구현하고, Facade 객체로 할당된 작업을 실제로 처리하지만 Facade에 대한 아무런 정보가 없음.
협력 방법
- 사용자는 Facade에 정의된 인터페이스를 이용해서 상호작용함. 서브시스템을 구성하는 객체가 실제의 요청 처리를 담당하지만 퍼사드는 서브시스템에게 메시지를 전달하기 위해 자신의 인터페이스에 동일한 작업을 정의해야 함.
- 퍼사드를 사용하는 사용자는 서브시스템을 구성하는 객체로 직접 접근하지 않아도 됨.
결과
- 서브시스템의 구성요소를 보호할 수 있음. 사용자가 다루어야 할 객체의 수가 줄어듬.
- 서브시스템과 사용자 코드 간의 결합도를 더욱 약하게 만듬. 서브시스템 내 정의된 요소들은 강하게 결합될 수 있음. 서브시스템의 사소한 변경으로 인한 재컴파일 시, 컴파일 의존성을 최소화할 수 있음.
- 응용프로그램 쪽에서 서브시스템 클래스를 사용하는 것을 완전히 막지는 않음. Facade를 사용할지 서브시스템 클래스를 직접 사용할지 결정할 수 있음.
구현 / 고려 사항
- 사용자와 서브시스템 간의 결합도 줄이기
- Facede를 추상 클래스로 정의. Facade의 서브클래스를 만들어 서브시스템에 대한 구체화.
- 사용자는 Facade만 사용하지 때문에 어떤 서브시스템의 구현이 사용되는지 알 필요 없음.
- 서브시스템 클래스 중 공개할 것과 감출 것
- 서브시스템도 클래스처럼 공개용 인터페이스가 있고 서브시스템 자체인 비공개 인터페이스가 있음. 무엇을 공개할 지 비공개로 할지 고민해봐야 함.
- 서브시스템 클래스를 비공개로 하는 것을 매우 유용하지만 많은 객체지향 언어가 이런 기능을 제공하지는 않음. 반대로 전체 공개는 클래스에 대한 네임 스페이스를 추가함으로써 가능함.
예제 코드
- 좋은 밤을 만들어주는 GoodNightMaker를 만들고 그 안에 HaveAGoodNight 메서드를 만들겠습니다.
- 인자로 받은 대상에 대해 좋은 밤을 보낼 수 있도록 갖가지 치료를 해주겠습니다. 이 때 HavaAGoodNight 메서드 내에서 좋은 밤을 보낼 수 있도록 여러 객체들이 도움을 줍니다.
#include "Facade1.h"
#include <iostream>
int main()
{
Man man;
GoodNightMaker goodnight_maker;
std::cout << "남자의 행복도 : " << man.GetHappiness() << std::endl;
goodnight_maker.HaveAGoodNight(&man, 8, Bed::Type::soft);
std::cout << "남자의 행복도 : " << man.GetHappiness() << std::endl;
return 0;
}
- 남자가 잘자는 기능을 해주는 Facade 객체가 HavaAGoodNight 메서드를 통해 남자의 행복감을 올려줬습니다.
//Facade1.h
#pragma once
class Man
{
public:
void AddHappiness(int happiness) { this->happiness += happiness; }
int GetHappiness() { return happiness; }
private:
int happiness = 0;
};
class Sleep
{
public:
bool EnsureGoodNight(Man* man, int hours);
};
class GirlFriend
{
public:
void GoodNightKiss(Man* man);
};
class Bed
{
public:
enum class Type
{
hard,
soft
};
public:
Bed(Type type) : type(type) {}
public:
bool IsSoftBed() { return type == Type::soft; }
private:
Type type;
};
class GoodNightMaker
{
public:
void HaveAGoodNight(Man* man, int sleep_horus, Bed::Type bed_type);
};
- 남자가 잘 잘 수 있게끔 도와주는 Sleep, GirlFriend, Bed 라고 했을 때 (이 3가지를 위에서 설명하던 서브시스템이라고 합시다.) 이 3가지를 한데 모아 GoodNightMaker 내의 HaveAGoodNight 메서드 내에서 남자에게 좋은 잠을 선사합니다.
//Facede1.cpp
#include "Facade1.h"
#include <iostream>
void GoodNightMaker::HaveAGoodNight(Man* man, int sleep_horus, Bed::Type bed_type)
{
GirlFriend girl_friend;
Sleep sleep;
Bed bed(bed_type);
girl_friend.GoodNightKiss(man);
if (sleep.EnsureGoodNight(man, sleep_horus) == false)
{
std::cout << "충분한 잠을 자지 못했습니다." << std::endl;
}
if (bed.IsSoftBed())
{
man->AddHappiness(20.0f);
}
}
bool Sleep::EnsureGoodNight(Man* man, int hours)
{
if (hours >= 8)
{
man->AddHappiness(hours * 5.0f);
return true;
}
else
{
return false;
}
}
void GirlFriend::GoodNightKiss(Man* man)
{
man->AddHappiness(30.0f);
}
- HaveAGoodNight 메서드 내에서 GirlFriend, Sleep, Bed 객체(외부에서는 몰라도 되는 서브시스템)를 이용하여 남자에게 좋은 잠을 선사합니다.
- 대신, 메서드 내에서 객체들이 강하게 결합되어 동작하는 모습을 볼 수 있습니다.
- 반대로, main의 관점에선 서브시스템들과 약하게 결합될 수 있었습니다.
'디자인 패턴 > 구조' 카테고리의 다른 글
프록시(Proxy) (0) | 2024.01.02 |
---|---|
플라이급(Flyweight) (0) | 2024.01.02 |
장식자(Decorator) (0) | 2023.12.22 |
복합체(Composite) (2) | 2023.12.21 |
가교(Bridge) (2) | 2023.12.21 |