mojo's Blog

Factory Method Pattern & Abstract Factory Pattern 본문

Design Patterns

Factory Method Pattern & Abstract Factory Pattern

_mojo_ 2024. 6. 9. 00:48

Creational patterns

- 새 연산자를 명시적으로 사용하지 않고 새 객체를 만들 수 있음

- 클라이언트 코드를 수정하지 않고도 다양한 객체를 객체화할 수 있음

- ex) Factory Method, Abstract Factory, Singleton, Builder, Prototype

 

Factory Method

- 상속을 사용하여 객체화할 객체를 결정함

 

Abstract Factory

- 객체 생성을 팩토리 객체에 위임함

 

Factory Method Pattern

 

Hamburger orderHamburger(String type) {
	Hamburger hamburger;
    
    if (type.equals("cheese"))
    	hamburger = new CheeseHamburger();
    else if (type.equals("greek"))
    	hamburger = new GreekHamburger();
    else if (type.equals("pepperoni"))
    	hamburger = new PepperoniHamburger();
    
    hamburger.prepare();
    ...
    hamburger.box();
    
    return hamburger;
}

 

타입에 따른 햄버거 객체를 생성하여 리턴하는 코드이다.

햄버거를 타입에 따라 객체를 생성하는 단순한 팩토리를 생성하면 다음과 같다.

 

public class SimpleHamburgerFactory {
	public Hamburger createHamburger(String type) {
    	Hamburger hamburger;
    
        if (type.equals("cheese"))
            hamburger = new CheeseHamburger();
        else if (type.equals("greek"))
            hamburger = new GreekHamburger();
        else if (type.equals("pepperoni"))
            hamburger = new PepperoniHamburger();
            
        return hamburger;
    }
}

 

위와 같이 팩토리를 활용하여 햄버거 객체를 생성해내는 메서드가 있다.

이를 활용하여 햄버거를 주문하는 코드를 수정하면 다음과 같다.

 

public class HamburgerStore {
	SimpleHamburgerFactory factory;
    
    public HamburgerStore(SimpleHamburgerFactory factory) {
    	this.factory = factory
    }
	public Hamburger orderHamburger(String type) {
		Hamburger hamburger;
    	hamburger = factory.createHamburger(type);
        
    	hamburger.prepare();
    	...
    	hamburger.box();
    
    	return hamburger;
    }
}

 

위와 같은 팩토리 구조를 다이어그램으로 나타내면 다음과 같다.

 

 

※ Franchising the hamburger store

다른 스타일로 NY, Chicago, California 등이 있다고 한다면,

다른 햄버거 팩토리로는 아래와 같이 만들 수 있다.

- NYHamburgerFactory

- ChicagoHamburgerFactory

- CaliforniaHamburgerFactory

 

NYHamburgerFactory nyFactory = new NYHamburgerFactory();
HamburgerStore nyStore = new HamburgerStore(nyFactory);
nyStore.order("Cheese");

ChicagoHamburgerFactory chicagoFactory = new ChicagoHamburgerFactory();
HamburgerStore chicagoStore = new HamburgerStore(chicagoFactory);
chicagoStore.order("greek");

 

목적: 객체를 만드는 방법을 노출하여 하위 클래스가 실제 생성 프로세스를 제어할 수 있도록 함

사용시기

- 클래스는 어떤 클래스를 만들어야 하는지 알 수 없음

- 하위 클래스는 생성할 객체를 지정할 수 있음

- 부모 클래스는 생성을 하위 클래스로 미뤄야 함

 

Requirement Change

이제 가게와 햄버거 생성을 연결하는 틀을 만들 필요가 있다.

 

public abstract class HamburgerStore {
	public Hamburger orderHamburger(String type) {
		Hamburger hamburger;
    	hamburger = createHamburger(type);
        
    	hamburger.prepare();
    	...
    	hamburger.box();
    
    	return hamburger;
    }
    protected abstract Hamburger createHamburger(String type);
}

 

예를 들어 햄버거를 준비하는 서비스를 제공하는 OO framework 이다.

프레임워크는 createHamburger() 팩토리 메서드를 호출하여 잘 정의하고 일관된 프로세스를 사용하여 준비할 수 있는 햄버거를 만든다.

프레임워크의 "클라이언트"는 이 클래스를 하위 클래스로 분류하고 createHamburger() 메서드의 구현을 제공한다.

구체적인 "product" 클래스에 대한 종속성은 하위 클래스에 캡슐화된다.

 

// NYHamburgerStore -> HamburgerStore
public Hamburger createHamburger(String type) {
	Hamburger hamburger;
    if (type.equals("cheese")) {
    	hamburger = new NYStyleCheeseHamburger();
    } else if (type.equals("greek")) {
    	hamburger = new NYStyleGreekHamburger();
    }
    return hamburger;
}

// ChicagoHamburgerStore -> HamburgerStore
public Hamburger createHamburger(String type) {
	Hamburger hamburger;
    if (type.equals("cheese")) {
    	hamburger = new ChicagoStyleCheeseHamburger();
    } else if (type.equals("greek")) {
    	hamburger = new ChicagoStyleGreekHamburger();
    }
    return hamburger;
}

 

팩토리 메서드를 사용하여 햄버거를 주문하는 순서는 아래와 같다.

1. HamburgerStore 객체를 생성한다.

     HamburgerStore nyHamburgerStore = new NYHamburgerStore();

2. orderHamburger 메서드를 호출한다.

     nyHamburgerStore.orderHamburger("cheese");

3. orderHamburger 메서드는 createHamburger 메서드를 호출한다.

     Hamburger hamburger = createHamburger("cheese");

4. 햄버거를 생성하면, prepare 및 box 등 주문 준비를 한다.

     hamburger.prepare() -> ... -> hambureger.box()

 

 

- Creator Class: NYHamburgerStore, ChicagoHamburgerStore

- Product Class: Hamburger

 

※ Design Principle: Dependency Inversion Principle

- 추상화에 의존하며, 구체적인 클래스에 의존하지 않음

- 높은 수준의 컴포넌트는 낮은 수준의 컴포넌트에 의존해서는 안 됨

- 팩토리 메서드는 의존성 반전 원리를 따르는 한 가지 방법

 

※ Factory Method Pattern

하위 클래스에서 객체화할 클래스를 결정할 수 있다.

- Create 클래스는 실제 Concrete Product 클래스를 객체화할 수 없는 방식으로 작성됨

- 객체화할 Concrete Product 클래스는 응용 프로그램에서 객체화하여 사용하는 Concrete Product 클래스에

   따라 결정됨

- 하위 클래스가 생성할 Concrete Product 클래스를 런타임에 결정한다는 의미는 아님

 

Abstract Factory Pattern

 

목적: 특정 객체를 전달하기 위해 생성 호출을 하나 이상의 구체 클래스에 위임하는 인터페이스 제공

사용시기

- 객체의 생성은 객체를 활용하는 시스템과 독립적이여야 함

- 시스템은 여러 객체 제품군을 사용할 수 있어야 함

- 객체 패밀리는 함께 사용해야 함

- 라이브러리는 구현 세부 정보를 노출하지 않고 게시해야 함

- 구체 클래스는 클라이언트와 분리되어야 함

 

추상 팩토리 패턴은 팩토리 메서드 패턴보다 한 단계 높은 추상화이다.

- 추상 팩토리 패턴: 컴포지션 사용

- 팩토리 메서드 패턴: 상속 사용

 

※ Participants

 

 

- AbstractFactory: 추상적 제품 객체를 만드는 작업에 대한 인터페이스 선언

- ConcreteFactory: 구체적인 제품 객체를 만들기 위한 작업 구현

- AbstractProduct: 제품 객체 유형에 대한 인터페이스 선언

- ConcreteProduct: 해당 구체 팩토리에서 생성할 제품 객체 정의

- Client: AbstractFactory 및 AbstractProduct 클래스에서 선언한 인터페이스만 사용

 

※ When to Use Abstract Factory Pattern?

1. 생성 패턴에 공통적인 것

- 시스템은 제품이 생성, 구성 및 표현되는 방식과 독립적이여야 함

- 클래스가 생성해야 하는 객체의 클래스를 예측할 수 없음

 

2. 추상 팩토리 패턴을 특정화

- 시스템은 제품군 중 하나를 사용해야 함

- 관련 제품 객체 제품군은 함께 사용할 수 있도록 설계되며 제약 조건을 적용해야 함

 

※ Consequences

장점

- 구체 클래스를 분리

- 제품군 교환이 용이함

- 제품 간의 일관성을 촉진함

 

단점

- 새로운 종류의 제품 지원이 어려움

 

'Design Patterns' 카테고리의 다른 글

Singleton Pattern  (1) 2024.06.09
Builder Pattern  (0) 2024.06.09
Mediator Pattern  (0) 2024.06.06
State Pattern  (0) 2024.06.06
Template Method Pattern  (0) 2024.06.06
Comments