구조 패턴 - 퍼사드 패턴 (Facade Pattern)

French Marigold·2024년 4월 9일
1

디자인패턴

목록 보기
5/10
post-thumbnail

정의

  • 퍼사드 패턴 (Facade Pattern)이란 기존 서브 클래스들의 많은 기능을 다 가져오는 것이 아니라 퍼사드를 통해서 서브 클래스들의 “핵심적인 기능만 따로 가져와” Client에서 사용할 수 있게끔 도와주는 디자인 패턴이다.
  • 예를 들어, A라는 프로그램 코드가 있다고 해보자. A 프로그램 코드에는 파일 변환, 비디오 추출, 영상 믹싱, 음향 마스터링과 같은 기능들이 있다. 우리 회사는 음향 회사이다. 그래서 A라는 프로그램 코드 중 음향 마스터링 코드가 상당히 마음에 들어서 이 프로그램 코드를 사용하고 싶다. 그럼 음향 마스터링 관련 코드를 사용하려고 해당 A 프로그램 코드를 전부 찍어와야 할까?
  • 이런 상황에서 필요한 디자인 패턴이 바로 퍼사드 패턴 (Facade Pattern)이다. 우리 회사는 퍼사드라는 중간 클래스 객체를 통해서만 A 프로그램 코드에 접근할 수 있으며 퍼사드는 A 프로그램 코드 중 음향 마스터링 관련 코드만 가져와서 사용할 수 있게 해주는 중간 객체의 역할을 해준다.

퍼사드 패턴 (Facade Pattern) 의 구조

  • Facade ⇒
    • Subsystem 객체들을 단순화하는 역할을 한다.
    • Subsystem의 핵심 메소드 및 코드들을 소유하고 있다.
    • 모든 클래스가 접근하기 용이하도록 Facade를 싱글톤으로 구성하는 것이 좋다. ⭐️
  • Additional Facade (Optional) ⇒
    • Facade 클래스가 반드시 한 개일 필요는 없으며, 기존 Facade와 전혀 연관이 없는 다른 종류의 기능이 있다면 다른 Facade를 추가해도 괜찮다.
    • 근데 Facade는 대부분의 경우 한 개만 있어도 충분하기 때문에 적합한 상황에만 사용하도록 하자.
  • Subsystem
    • 복잡한 기능과 구조를 가지고 있는 영역.
    • Subsystem은 Facade의 존재를 알 수 없다.
  • Client
    • Clients는 Subsystem과 직접 소통하는 것이 아니라, 오직 Facade를 통해서만 Subsystem에 접근할 수 있음.

퍼사드 패턴 적용 과정 예시

  1. 우선 아까 이야기한 A 프로그램 코드의 내부를 먼저 살펴보자. A 프로그램에는 여러 가지 기능이 존재한다. 다시 말하지만 A 프로그램 코드 중 일부를 회사에서 사용하기 위해 A 프로그램 전체 인스턴스를 사용하는 것은 적절하지 않다.
class AProgram {
    func covertFile(_ fileName: String) {
        print("\(fileName) 파일을 변환하였습니다")
    }
    
    func extractVideo(_ videoFileName: String) {
        print("\(videoFileName)을 추출 완료하였습니다")
    }
    
    func mixingVideo(_ videoFileName: String) {
        print("\(videoFileName)을 믹싱하였습니다")
    }
    
    func masteringSound(_ mp3FileName: String) {
        print("\(mp3FileName)을 마스터링하였습니다")
    }
}
  1. Facade 클래스 객체를 하나 만든다. 우리 회사(Client)는 해당 Facade 객체 클래스를 통해서만 A 프로그램 코드와 소통할 것이다. Facade 객체가 만일 단 한 개만 존재할 경우 모든 객체에서 접근이 가능할 수 있도록 싱글톤으로 구성하는 것이 좋다.
class Facade {
		// 대부분의 경우 Facade 객체는 하나만 있어도 충분하다.
		// Facade 객체가 한 개일 경우 싱글톤으로 생성하는 것이 좋다. ⭐️⭐️
		static let shared = Facade()

    // Client가 Subsystem을 직접적으로 알지 못하도록 private 처리 ⭐️⭐️
    private let aProgram = AProgram()
    
    func masteringSound(_ mp3FileName: String) {
        aProgram.masteringSound(mp3FileName)
    }
}
  1. 우리 회사 내부 코드 (Client)에 Facade 객체 인스턴스를 생성한다. 그 후, 우리 회사 코드 내에 메소드를 생성한 후, A 프로그램의 코드를 마음껏 사용하면 된다.
class OurCompany_Client {
    let facade = Facade.shared
    
    // Facade를 통해서 Subsystem의 코드를 우리 코드 내에서 사용할 수 있게 되었다!
    func masterSound() {
        facade.masteringSound("iOS.mp3")
    }
}

패턴 사용 시기

  • 복잡한 시스템 내의 코드 중 일부만 따로 빼내어 활용하고 싶을 때 사용한다.
  • 복잡한 시스템과의 결합도를 줄이면서 사용하고자 하는 기능을 최대로 활용하고자 할 때.

패턴의 장점

  • Facade라는 중간 객체를 이용해 분리 작업을 거치기 때문에 훨씬 가볍고 편리하게 외부 코드를 활용할 수 있다.
  • 복잡한 Subsystem 코드는 감춤으로써, Client가 Subsystem의 전체 코드를 모르더라도 Facade 클래스만 이해하면서 사용 가능하다.

패턴의 단점

  • Facade가 앱 내의 모든 클래스에 관여하는 전능한 객체가 될 수 있음. (Facade가 하는 일이 너무 많다.)
  • Facade가 어쨌든 Subsystem을 알아야 하기 때문에 의존성을 피할 수는 없다.

참고 문헌

profile
꽃말 == 반드시 오고야 말 행복

4개의 댓글

comment-user-thumbnail
2024년 4월 12일

파사드(facade)가 프랑스어로 건물의 외관이라는 뜻이래요
그래서 내부가 어떻게 생겼든 외관만 보면 된다는 의미로 파사드 패턴으로 명명했다고 하더라고요
(https://en.wikipedia.org/wiki/Facade_pattern)

“Facade가 단 한 개만 존재할 경우”, 모든 클래스가 접근하기 용이하도록 Facade를 싱글톤으로 구성한다.

이 문장을 좀 더 설명해 주실 수 있을까요?
Facade는 보통 한 인스턴스로 여러군데에서 사용하도록 하나요?

1개의 답글
comment-user-thumbnail
2024년 4월 13일

글을 읽으며 이제껏 인지 없이 사용하고 있던 작성법에 퍼사드 패턴 이라는 명칭이 있었음을 알게 되었습니다!
또 싱글톤을 사용한 예시를 들어주셨는데요, 이전에는 생성자를 private 으로 처리하지 않으면 싱글톤의 의도 (단 한개만 만들겠다)가 망가지는 거 아닌가, 애초에 싱글톤의 의도가 없는 게 아닌가 빡빡하게 생각한 적이 있었습니다.
하지만 다양한 예시를 접하며 (물론, 실제 작업에선 지켜주는 것이 좋다고 생각합니다...!) 그 의도가 통한다면 유연하게 해석하는 것도 힘임을 깨닫습니다.
퍼사드 패턴! 멋진 지식 알려주셔서 감사합니다!
+) 디자인패턴에 관해 정리해 주신 글들 읽으며 Client <- 라는 표기에 적응해 갑니다 ㅎㅎ 다른 분들의 코드를 읽을 때에 그 명칭의 이유가 궁금했었는데... 감사합니다!

1개의 답글