본문바로가기

 

Creational Patterns

Factory Method

Definition

객체를 생성하기 위해 인터페이스를 정의하지만, 어떤 클래스의 인스턴스를 생성할지에 대한 결정은 서브클래스에서 이루어지도록 Factory Method 패턴은 서브클래스에게 인스턴스 생성의 책임을 미룬다.

분류

설명

이름(Name)

Factory Method

의도(Insert)

객체를 생성하기 위해 인터페이스를 정의하지만, 어떤 클래스의 인스턴스를 생성할지에 대한 결정은 서브클래스에서 이루어지도록 Factory Method 패턴은 서브클래스에게 인스턴스 생성의 책임을 미룬다.

문제점(Problem)

해결법(Solution)

관계자(Participant)

협력자(Collaborator)

Abstract Factory 패턴은 이 Factory Method를 이용해서 구현하는 경우가 많다. Factory Method는 또한 나중에 행위 패턴에 정의될 Template Method 패턴에서도 사용되는 경우가 많다. Creator 클래스는 객체의 초기화를 위해 초기화 오퍼레이션을 사용하지만, Factory Method 패턴은 이런 오퍼레이션을 필요로 하지 않는다.

결과(Consequence)

애플리케이션에 국한된 클래스가 여러분이 코드에 종속될 필요를 없애준다. 애플리케이션은 Product 클래스에 정의된 인터페이스와만 동작하도록 코드를 작성하고, 이후 사용자가 정의한 ConcreteProduct 클래스와 동작할 수 있게 된다.

이 팩토리 메소드의 단점은 클라이언트가 Creator 클래스를 반드시 상속하여 구체적 제품을 생성해야 한다는 것이다. 서브클래싱 기법으로 클라이언트가 Creator 클래스를 상속해서 서브 클래스를 만들어야 할 때는 상관 없지만, 서브클래싱이 안 되는 경우라면 클라잉너트는 다른 방식으로 클래스의 진화 과정을 처리해야 한다.

추가적인 두개의 결과를 만든다.

1. 서브클래스에 대한 Hook Method를 제공한다. Factory Method로 클래스 내부에서 객체를 생성하도록 하는 것이 객체를 긱접 생성하게 하는 것보다 훨씬 응용성이 높아진다. Factory Method 패턴에서는 객체별로 서로 다른 버전을 제공하는 후크 기능을 서브클래스에 정의한다.

2. 병렬적 클래스 계층도를 연결하는 역할을 담당한다. Creator 클래스만이 Factory Method를 호출한다. 병렬적 클래스 계층도는 클래스가 자신이 책임 행위를 분리된 다른 클래스에 의뢰하는 경우에 발생한다. 사용자와 대화식으로 처리되는 그래픽 객체를 생각해 보자. 그래픽 객체는 마우스로 크기를 조정하고 뒤집고 이동할 수 있다. 이러한 상호작용을 구현하는 것은 항상 쉬운 일만은 아니다. 이런 구현의 어떤 시점에서는 조작의 상태를 저장하고 조작에 필요한 정보를 저장하고 수정하는 구현이 필요하게 된다. 이 상태는 조작 과정 중에만 필요하다. 그러나 이 상태가 그림 객체 자체에 저장될 필요는 없다. 서로 다른 그림은 서로 다르게 동작해야 한다. , 선의 길이를 늘리는 것과 글자의 크기를 크게하는 것은 다른 방식으로 구현해야 한다.

구현(Implementation)

1. 두가지 다른 방법이 있을 수 있다. Factory Method 패턴을 구현하는 두 가지 방식은 (1) Creator클래스를 추상 클래스로 정의하고 정의한 Factory Method에 대한 구현은 제공하지 않는 경우와 (2) Creator가 슈퍼클래스가 아닌 서브클래스로 정의되어 Factory Method에 대한 기본 구현을 제공하는 경우이다. 추상 클래스로 정의하는 경우는 구현을 제공한 서브클래스를 반드시 정의 해야한다. 이때의 문제는 아직 예측할 수 없는 클래스들을 생성해야 하는 경우이다. 서브클래스로 정의하는 경우는 Creator Factory Method를 사용하여 유연성을 보장 할 수 있다. , 객체의 생성은 별도의 오페레이션으로 분리하고, 이 오퍼레이션은 서브클래스에서 재정의함으로써 서브클래스 설계자는 부모 클래스가 인스턴스를 만드는 개게의 클래스를 변경 할 수 있다.

2. Factory Method Parameterize. 또 다른 방식은 Factory Method를 이용하여 여러 종류의 제품을 생성하게 할 수도 있다. Factory Method가 파라미터를 받아서 어떤 종류의 제품을 생성할 지를 식별한다. 물론, Factory Method가 생성하는 모든 객체는 Product이라는 인터페이스를 만족해야 한다. Document 예제로 본다면 Application 클래스는 서로 다른 종류의 문서를 지원해야 하는데 이때 CreateDocument() Method에 생성할 문서 객체의 오류를 파라미터로 넘겨주면 된다.

3. 언어마다 구현의 차이를 보일 수 있다. 스몰토크 프로그램은 종종 인스턴스화 할 객체의 클래스를 반환하는 메소드를 사용한다. Create Factory Method는 이 클래스를 이용해서 제품을 생성하고, ConcreteCreator 클래스는 이 값을 저장하거나 연산한다. 결과적으로 인슨턴스화될 ConcreteProduct의 타입과의 늦은 조합이 이루어지게 된다.

4. Templet을 사용하면 상속을 사용하지 않아도 된다. Factory Method의 문제점 중하나는 어떤 Product 클래스를 하나 추가하게 되면 이를 새로운 서브클래스로 생성해야 하는 것이다. 이로써 클래스 계층도가 확장되는 문제를 생길 수 있다. C++에서 이런 문제를 해결할 수 있는 방법 중 하나는 Templet Class를 정의하고 여기에 Product 클래스의 서브 클래스로 파라이멑화 하는 방법이다.

5. 명명 규칙을 따르는 것도 매우 중요한 일이다. MacApp 와 같은 매킨토시 애플리케이션 프레임워크는 Factory Method를 다음과 같은 규칙에 의해 정의하고 있다.

Class* DoMakeClass()

여기에서의 Class는 우리 예에서의 Product 클래스에 해당하는 것으로 생성해야 하는 클래스의 이름으로 대체 된다.

UML class diagram

 

 

Participants

The classes and/or objects participating in this pattern are:

  • Product (Page)
    • defines the interface of objects the factory method creates
  • ConcreteProduct (SkillsPage, EducationPage, ExperiencePage)
    • implements the Product interface
  • Creator (Document)
    • declares the factory method, which returns an object of type Product. Creator may also define a default implementation of the factory method that returns a default ConcreteProduct object.
    • may call the factory method to create a Product object.
  • ConcreteCreator (Report, Resume)
    • overrides the factory method to return an instance of a ConcreteProduct.
Sample code in C#

This structural code demonstrates the Factory method offering great flexibility in creating different objects. The Abstract class may provide a default object, but each subclass can instantiate an extended version of the object.

// Factory Method pattern -- Structural example

//--------------------------------------------------------

// Copyright (C) 2001 - 2002, Data & Object Factory

// All rights reserved. www.dofactory.com

//

// You are free to use this source code in your

// applications as long as the original copyright

// notice is included.

//

// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT

// WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED,

// INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF

// MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.

//--------------------------------------------------------

using System;

using System.Collections;

// "Product"

abstract class Product

{

}

// "ConcreteProductA"

class ConcreteProductA : Product

{

}

// "ConcreteProductB"

class ConcreteProductB : Product

{

}

// "Creator"

abstract class Creator

{

// Methods

abstract public Product FactoryMethod();

}

// "ConcreteCreator"

class ConcreteCreatorA : Creator

{

// Methods

override public Product FactoryMethod()

{

return new ConcreteProductA();

}

}

// "ConcreteCreator"

class ConcreteCreatorB : Creator

{

// Methods

override public Product FactoryMethod()

{

return new ConcreteProductB();

}

}

/// <summary>

/// Client test

/// </summary>

class Client

{

public static void Main( string[] args )

{

// FactoryMethod returns ProductA

Creator c = new ConcreteCreatorA();

Product p = c.FactoryMethod();

Console.WriteLine( "Created {0}", p );

// FactoryMethod returns ProductB

c = new ConcreteCreatorB();

p = c.FactoryMethod();

Console.WriteLine( "Created {0}", p );

}

}

Output

Created ConcreteProductA
Created ConcreteProductB

This real-world code demonstrates the Factory method offering flexibility in creating different documents. The derived Document classes Report and Resume instantiate extended versions of the Document class. Here, the Factory Method is called in the constructor of the Document base class.

// Factory Method pattern -- Real World example

//--------------------------------------------------------

// Copyright (C) 2001 - 2002, Data & Object Factory

// All rights reserved. www.dofactory.com

//

// You are free to use this source code in your

// applications as long as the original copyright

// notice is included.

//

// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT

// WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED,

// INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF

// MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.

//--------------------------------------------------------

using System;

using System.Collections;

// "Product"

abstract class Page

{

}

// "ConcreteProduct"

class SkillsPage : Page

{

}

// "ConcreteProduct"

class EducationPage : Page

{

}

// "ConcreteProduct"

class ExperiencePage : Page

{

}

// "ConcreteProduct"

class IntroductionPage : Page

{

}

// "ConcreteProduct"

class ResultsPage : Page

{

}

// "ConcreteProduct"

class ConclusionPage : Page

{

}

// "ConcreteProduct"

class SummaryPage : Page

{

}

// "ConcreteProduct"

class BibliographyPage : Page

{

}

// "Creator"

abstract class Document

{

// Fields

protected ArrayList pages = new ArrayList();

// Constructor

public Document()

{

this.CreatePages();

}

// Properties

public ArrayList Pages

{

get{ return pages; }

}

// Factory Method

abstract public void CreatePages();

}

// "ConcreteCreator"

class Resume : Document

{

// Factory Method implementation

override public void CreatePages()

{

pages.Add( new SkillsPage() );

pages.Add( new EducationPage() );

pages.Add( new ExperiencePage() );

}

}

// "ConcreteCreator"

class Report : Document

{

// Factory Method implementation

override public void CreatePages()

{

pages.Add( new IntroductionPage() );

pages.Add( new ResultsPage() );

pages.Add( new ConclusionPage() );

pages.Add( new SummaryPage() );

pages.Add( new BibliographyPage() );

}

}

/// <summary>

/// FactoryMethodApp test

/// </summary>

class FactoryMethodApp

{

public static void Main( string[] args )

{

Document[] docs = new Document[ 2 ];

// Note: constructors call Factory Method

docs[0] = new Resume();

docs[1] = new Report();

// Display document pages

foreach( Document document in docs )

{

Console.WriteLine( "\n" + document + " ------- " );

foreach( Page page in document.Pages )

Console.WriteLine( " " + page );

}

Console.Read();

}

}

Output

Resume -------
SkillsPage
EducationPage
ExperiencePage
Report -------
IntroductionPage
ResultsPage
ConclusionPage
SummaryPage
BibliographyPage