GoF의 디자인 패턴, 파사드 패턴에 대해 알아본다.
해당 글은, 다음의 코드를 기반으로 이해하는 것이 편리합니다.
핵심 요약

- 어떤 기능을 처리하기 위해 여러 객체들 사이의 복잡한 메서드 사용을 감추는 패턴
- 즉, 사용하는 쪽에서는 여러 클래스를 알필요 없이 단순하게 사용할 수 있다.
- 블랙박스화 라고 생각해도 좋다.
- 라이브러리를 제공할 시 사용하면 좋다.
- 물론 사용하는 방법을 제한해서 제공하기 때문에, 어찌보면 사용하는 쪽에서 유연성이 떨어진다고 생각할 수도 있겠다.
- 다만 보통은 서브 시스템 클래스 접근도 열어주기 때문에 그렇게 생각은 잘 하지 않는 편.
예시

- Class Diagram이 어렵다면 여기를 참고하자.
- 해당 코드는 DB에서 특정 이름을 기반으로 자료를 조사하는 동작을 모사한 것이다.
- 이 때, 캐시를 사용하여 속도를 높히려 한다.
- 이 과정에서 캐시 조회, DB 조회, 캐시 삽입등 어떠한 로직이 필요하다.
- 만약 이 과정을 사용하는 측에서 조합하도록 한다면 아래와 같은 더러운 코드가 발생한다.
// Not using Facade
internal func main() {
let dbms = DBMS()
let cache = Cache()
let name = "wansik"
if let row = cache.get(with: name) {
let message = Message(row: row)
print(message.makeName())
print(message.makeBirthday())
print(message.makeEmail())
} else if let row = dbms.query(name: name) {
cache.put(row: row)
let message = Message(row: row)
print(message.makeName())
print(message.makeBirthday())
print(message.makeEmail())
} else {
print("없는 이름입니다..")
return
}
}
- 어떻게 사용해야 하는지에 대해 추가로 조사해야 하기 때문에 사용성 측면에서 좋지 못하다.
- 개발자가 하나의 클래스를 만들고, 그 안에서 어떻게 동작할지에 정의해두면 편하지 않을까?

// Using Facade
internal struct Facade {
internal func run(name: String) {
if let row = cache.get(with: name) {
let message = Message(row: row)
print(message.makeName())
print(message.makeBirthday())
print(message.makeEmail())
} else if let row = dbms.query(name: name) {
cache.put(row: row)
let message = Message(row: row)
print(message.makeName())
print(message.makeBirthday())
print(message.makeEmail())
} else {
print("없는 이름입니다..")
return
}
}
private var dbms = DBMS()
private var cache = Cache()
}
internal func main() {
let facade = Facade()
facade.run(name: "wansik")
}- Facade 객체 안에 해당 함수의 내용을 이동했다.
- 외부에서는
run(name:)만 호출하면 된다.
동기

- 컴파일러 시스템을 생각해보자.
- Parser, Scanner 등의 여러 작업을 거쳐야 비로소 바이너리가 만들어진다.
- 하지만 사용하는 쪽에서는
compile()메서드 하나만 알면된다.
활용성
- 단순한 인터페이스 제공이 필요할 때
- 재사용을 위한 작은 클래스를 만들었으나, 실제 사용하는 것이 어려워졌을 때
- 서브시스템 간의 결합도를 줄이고 싶을 때
- 서브시스템을 계층화 시키고 싶을 때 묶어서 접근점을 제공하여 종속성을 줄일 수 있음
구조

참여자
- Facade(
Facade)- 단순하고 일관된 통합 인터페이스 제공
- 서브 시스템을 구성하는 클래스가 어떤 클래스를 요청해야 하는지 알고 있음
- 위의 예시로부터 생각해보면 원래는 main에서 개발자가 하다가 옮긴 것 뿐임
- 즉, 만든 개발자가 어떻게 동작하는지 다 알고 코드를 옮겨둔 것
- 사용자의 요청을 서브시스템 객체들에게 전달함
- Subsystem(
DBMS,Cache)- 서브시스템의 기능을 구현함
- 작업을 실제로 처리하지만 Facade에 대한 아무런 정보가 없음
협력 방법
- 사용자는 Facade에 정의된 인터페이스를 사용하여 상호작용함
- Facade는 알맞은 객체에게 해당 작업을 전달함
- 사용자는 서브시스템을 구성하는 객체로 직접 접근할 필요가 없음
결과
- 서브 시스템의 구성 요소를 보호할 수 있다.
- 서브 시스템과 사용자 코드간의 결합도를 약하게 만든다.
- 응용프로그램 쪽에서 서브 시스템 클래스를 사용하는 것을 완전히 막지는 않는다.
관련 패턴과 차이점
- 추상 팩토리(03. Abstract Factory)
- 서브 시스템에 독립적인 방법으로 객체 생성 인터페이스를 제공하기 위해 함께 사용 가능
- 중재자(20. Mediator) - 기존에 존재하는 클래스의 기능성을 추상화하는 면에서 유사, 하지만
- 중재자: 객체들간의 협력 관계를 추상화하여 기능의 집중을 막자.
- 13. Facade: 서브시스템 인터페이스 자체를 추상화하여 사용을 용이하게 하자.
- 단일체(08. Singleton) - 하나만 있어도 되면 싱글톤으로 구현
생각해볼 점
- UIKit과 같은 라이브러리가 이런식으로 많이 되어있다.
- 사용하기 편하게 하기 위해
UILabel같은 것을 제공하지만, - 실질적으로 아랫단은
CoreText와 같은 것으로 되어있다. - 맞는 예가 아닐 수 있으나, 결국 사용성을 위해 Interface의 제약을 걸었다는 점에서 유사하다 할 수 있다.