Abstract Factory(31)


ABSTRACT FACTORY(DP 87)


Intent

연관된 혹은 의존적인 객체들의 집합을 만들기 위한 인터페이스를 제공한다. 클라이언트가 구체적인 클래스들에 대한 구체화
를 하지 않고 추상적으로 어떤 상품의 집합(여러 종류의 자동차들)에서 상품(자동차)을 만든다고 하자.

Structure

Discussion

패턴은 실제로 아주 간단하다. 문제 상황은 많은 부분들을 포함한다. Abstract Factory 패턴이 적용될 수 있는 예제를 보도록
하자.

우선 첫째로, 우리는 컴포넌트의 하위 부분들로 부터 단계별로 상품(자동차)을 만들 필요가 있는 응용 프로그램을 만들어보자.
그것은 몸체, 엔진, 변속장치, 그리고 승객 칸막을 포함하는 자동차를 만드는 것이다. 둘째로, 응용 프로그램은 같은 상품의
부분들을 원한다. 즉 Ford 자동차는 Ford 엔진과 변속장치를 가져야한다. 이것은 Ford Family안의 부분들이다. 셋째, 우리는
여러 part의 family들을 가진다.
Ford parts,Toyota parts,Porsche parts 등등이다. 유사한 클래스들이 클래스 구조를 통해서 확장된다. 각각이 적당한 하위
구조를 가지게 된다. 가령 CarEngine 하위 구조의 엔진들, CarBody 구조의 body 등등을 가지게 된다.
(결국, 각각이 CarEngine을 Base Class로 해서 상속을 통해 Ford Engine,Toyota Engine등등으로 확장될 수 있다는 말이다.)
따라서, 우리는 다른 집합의 부분들을 선택하지 않고, 하나의 집합(Family)으로부터 각각의 자동차 부분들을 쉽게 얻을 수
있는 방법이 필요하다. (Toyota 엔진에서는 Ford 자동차가 작동할 수 없다.)
그리고 모든 faimily에서 같은 코드를 사용해서 부분들을(엔진,변속장치..) 얻는 것을 사용한다.
우리는 Abstract Factory Pattern을 이용해서 두가지 목표를 이룰 수 있다.
(정리 : Abstract Factory Pattern은 Factory Pattern을 추상화시킨 것이다.Factory Pattern의 목적이 Base Class로부터 상속
된 여러 종류의 클래스들 중 하나를 선택할 수 있도록 해주는 것을 의미한다.
Abstract Factory Pattern는 여러 개의 Factory중 하나를 선택할 수 있도록 해주는 것을 수행한다. )
우리는 아래와 같은 자동차와 자동차 부분들의 클래스를 가지고 있다.


Vechile과 CarPart는 Object 클래스의 서브 클래스이다. 물론, 이 클래스 구조는 많은 단계에서 전체적으로 단순화된다.
자동차 회사들(Ford)은 자동차, 몸체, 엔진, 심지어 엔진 종류(가솔린 엔지 혹은 디젤 엔진)도 몇몇 다른 모델을 가지고 있다.
그러므로 우리가 여기서 보여주는 것 보다 현실 세계는 좀더 높은 추상을 가지고 있을 것이다. 하지만, 우리의 패턴 묘사를
적용하기에는 충분한 수준의 추상을 제공하고 있다.

우리는 CarPartFactory라는 추상 팩토리 클래스 정의를 하면서 패턴 구현을 시작한다. 이것은 "구체적인 클래스들에 대한
구체화 없이 관계된 혹은 의존적인 객체 집합을 만들기 위한 인터페이스를 제공하는" (Intent 부분에서 언급한 내용)
클래스이다. 그것은 추상적인 상품 생성 함수들(makeCar,makeEngine,makeBody)을 정의한다. 그 때 우리는 상품 집합 당
하나의 구체적인 팩토리 하위 클래스를 정의한다. 각각의 하위 클래스들은 적당한 부분을 만들고 반환하기 위해서 상품 생성
함수를 재 정의한다. 그래서 우리는 Object를 상속한 새로운 하위 구조를 추가한다.



자동차 부분(part) 생성 메쏘드를 구현하기 위해서, 우리는 추상 팩토리 클래스로 시작한다.

      CarPartFactory>>makeCar
          self subclassResponsibility

      CarPartFactory>>makeEngine
          self subclassResponsibility

      CarPartFactory>>makeBody
          self subclassResponsibility
그리고 이 메쏘드를 오버라이드하는 구체적인 하위 클래스를 추가한다.

      
      FordFactory>>makeCar
          ^FordCar new

      FordFactory>>makeEngine
          ^FordEngine new

      FordFactory>>makeBody
          ^FordBody new

      ToyotaFactory>>makeCar
          ^ToyotaCar new

      ToyotaFactory>>makeEngine
          ^ToyotaEngine new

      ToyotaFactory>>makeBody
          ^ToyotaBody new
전체적으로, 우리의 팩토리들은 아래와 같이 보인다.


Abstract Factory로, 부분들을(part) 결합시키는 것은 팩토리의 클라이언트가 하는 일이다. 팩토리는 부분들의(part) 하나의 집합의 (family)로 부터 나온다는 것을 보장한다. 하지만, 팩토리는 단지 부분을(part) 반환하는 일만 할 뿐이다. 최종 상품은 팩토리가 조립하지 않는다. 그것은 클라이언트의 일이다. (우리는 Abstract Factory와 Builder 패턴 사이의 주요한 차이점을 나중에 볼 것이다.)

CarAssembler 객체가 팩토리 클라이언트라고 추정해보자. 그리고 CarPartFactory 객체를 참조하는 팩토리라고 이름지어진 인스턴스 변수를 갖자.

    
         CarAssembler>>assembleCar
             | car |
             "Create the top-level part, the car object which starts out having no subcomponents, and add an engine, body, etc."
             
             car := factory makeCar
             car 
                 addEngine: factory makeEngine;
                 addBody: factory makeBody;
                 ...
             ^car
만약, 팩토리가 FordFactory의 인스턴스였다면, 자동차에 추가되기 위해 얻어진 엔진은 FordEngine일 것이다. 만약 팩토리가 ToyotaFactory였다면, ToyotaEngine은 팩토리의 makeEngine에 의해서 만들어 질 것이고, 그 때 자동차에 추가될 것이다.

아직, 확실하지 않는 한 부분이 있다. CarAssembler는(factory 클라이언트) 어떻게 구체적인 CarPartFactory 하위 클래스의 인스턴스를 얻을 수 있을까? 그것은 특별한 하위 클래스 자체를 소비자의 선택에 기초해서 인스턴스화 할 수 있을 것이다. 혹은 외부 객체에 의해서 팩토리 인스턴스를 다룰수도 있을 것이다.
하지만, 두 경우에 자동차를 생성하기 위한 코드와 그것의 컴포넌트 하위 부분은 여전히 같다. 즉, 모든 CarPartFactory 클래스들은 동일한 메시지 프로토콜을(다형성)을 구현하기 때문에, 팩토리 클라이언트는 팩토리 타입이 무엇인지 상관하지 않고 호출을 할 수 있다. 그것은 단지 팩토리 프로토콜에 의해 제공되는 일반적인 메시지를 전송한다.

다형성의 힘 때문에, 클라이언트는 코드 구현을 한번만 하면된다. ABSTRACT FACTORY PATTERN을 사용하지 않을 경우, 자동차 생성 코드는 다음과 같이 보일 것이다.(아주 비효율적인 코드)

         CarAssembler>>assembleCar
            "Without Abstract Factory."
            | car |
            car := (consumerChoice == #Ford
                       ifTrue:  [FordCar new]
                       ifFalse:  [consumerChoice == #Toyota
                           ifTrue:  [ToyotaCar new]
                           ifFalse:  [consumerChoice == #Porsche
                                  ifTrue:  [PorscheCar new]
                                  ifFalse:  [...])
            car addEngine:
                   (consumerChoice == #Ford
                       ifTrue:  [FordEngine new]
                       ifFalse:  [...]).
           
            ...
            ^car
따라서, CarAssmebler를 만들기 위한 자동차 종류가 무엇이고 그 하위 부분들이 무엇을 해야하고, 그것의 실제 부분의 인스턴스가 무엇을 수행해야 할지를 결정한다. ABSTRACT FACTORY 해결은 우리가 CarAssembler 객체 밖의 모든 행동들을 추상화시킨다. 그리고 팩토리로 분리한다. 특별한 자동차 팩토리로 CarAssembler 확인을 한 후에, CarAssembler는 간단하게 구체적인 자동차와 하위 부분을 만들기 위한 팩토리를 호출한다.

ABSTRACT FACTORY 접근은 좀더 모듈적이고, 좀더 쉽게 확장 가능한 디자인을 할 수 있다. 시스템에 새로운 타입의 자동차를 추가하기 위해서, 우리는 CarPartFactory의 서브 클래스를
추가하고 그것을 인스턴스화기 위한 코드가 필요할 뿐이다.

여기에 효과적인 두 개의 추상이 있다. 첫번째, 모든 CarPartFactory들이 같은 메시지 인터페이스를 구현한다. 클라이언트가 그들이 메시지를 보내기 위해서 CarPartFactory의 정확한
타입이 무엇인지 신경쓰지 않고 같은 생성 메시지를 보내는 것을 팩토리가 수행한다.
Retrieved from http://wiki.zeropage.org/wiki.php/DPSCChapter3
last modified 2021-02-07 05:23:04