• design-pattern
  • oop
  • python

생성 패턴: Abstract Factory와 Builder

추상 팩토리 패턴의 고전적 구현과 파이썬다운 구현을 비교하고, 빌더 패턴으로 복잡한 객체를 단계별로 생성하는 방법을 정리한다.

시리즈 · 디자인 패턴2 / 2
  1. Facade 패턴
  2. 생성 패턴: Abstract Factory와 Builder

추상 팩토리 패턴 (Abstract Factory)

여러 객체로 구성된 복합 객체를 만들어야 하고, 포함된 객체들이 모두 특정한 한 계통에 속할 때 사용한다.

고전적인 추상 팩토리

  • DiagramFactory: 일반 텍스트 형태의 다이어그램을 만드는 팩토리
  • SvgDiagramFactory: SVG 그래픽 형태의 다이어그램을 만드는 팩토리

다이어그램 팩토리를 인자로 받아 다이어그램을 생성하는 create_diagram 코드

create_diagram은 다이어그램 팩토리를 인자로 받아 다이어그램을 생성한다. 다이어그램 팩토리 인터페이스만 지원하면 어떤 종류의 팩토리가 들어와도 상관없다.

DiagramFactory와 SvgDiagramFactory의 구현 코드

두 팩토리 구현의 유일한 차이는 반환하는 객체의 타입이다. 한쪽은 Diagram 계열, 다른 쪽은 SvgDiagram 계열의 객체를 반환한다.

파이썬다운 방법

고전적인 구현에는 다음과 같은 문제가 있다.

  1. 어느 팩토리도 상태를 가질 필요가 없으므로 굳이 인스턴스를 생성할 필요가 없다.
  2. SvgDiagramFactory와 DiagramFactory의 코드가 거의 같고, 차이는 반환하는 인스턴스의 타입뿐이다.
  3. 이름 충돌을 피하려고 SVG 쪽 클래스에 접두어를 붙여야 했다. (예: SvgRectangle)

해결 방법은 두 가지다.

  • Diagram, Rectangle, Text 클래스를 DiagramFactory의 내부 클래스로 만든다.
  • 각 팩토리 메서드를 classmethod로 만든다.

내부 클래스와 classmethod를 사용한 DiagramFactory 구현 코드

이렇게 하면 DiagramFactory를 상속받은 SvgDiagramFactory는 make_diagram, make_rectangle 같은 메서드를 더 이상 오버라이드할 필요가 없다. 내부 클래스만 바꿔 정의하면 된다. 또한 classmethod이므로 팩토리 객체를 생성하지 않고도 다이어그램 인스턴스를 얻을 수 있다.

빌더 패턴 (Builder)

  • 복잡한 객체를 만드는 일을 돕는다.
  • 객체가 복잡하면 생성 과정을 단계별로 나눌 필요가 있다.

다양한 매개변수 조합으로 User를 생성해야 하면, 아래처럼 생성자가 조합마다 계속 늘어난다.

매개변수 조합마다 생성자가 늘어난 User 클래스 코드

이럴 때는 빌더로 User 객체를 만드는 편이 훨씬 효율적이다.

빌더 패턴으로 User 객체를 생성하는 코드

파이썬 예제: 폼 빌더

각 폼을 생성한 다음 적절한 파일에 저장하는 예제다.

폼을 생성해 파일로 저장하는 코드

create_login_form()에 적절한 빌더 객체를 인자로 전달해 호출한다.

빌더 객체를 인자로 받는 create_login_form 함수 코드

HtmlFormBuilder와 TkFormBuilder는 모두 추상 기반 클래스인 AbstractFormBuilder를 상속한다.

추상 기반 클래스 AbstractFormBuilder 코드

메타클래스로 abc.ABCMeta를 지정하면 해당 클래스는 인스턴스를 만들 수 없고 추상 기반 클래스로만 쓸 수 있다. 다만 실행 시 약간의 비용이 더 든다.

그래서 많은 파이썬 프로그래머는 덜 엄격한 방법을 쓴다. 메타클래스를 쓰지 않고, 해당 클래스를 추상 기반 클래스로만 사용해야 한다고 문서화하는 것이다. 강제성이 없으므로 문제가 될 여지는 있다.

Reference