Post List

2015년 1월 4일 일요일

Factory Method Pattern (팩토리 매서드 패턴)

Factory Method Pattern

개체 생성시 직접적으로 생성자를 호출하지 않고, Factory Method를 이용하여 생성한다.

유용한 경우

1. 구체적으로 어떤 클래스의 객체를 생성해야할지 미리 알지 못할 경우 유용하다. 구체적으로 생성될 객체는 하위 클래스에 의해 결정이 되며 하위 클래스는 계속 확장이 가능하다.
2. 클래스들에게 개별 객체의 생성 책임을 분산시켜 객체의 종류별로 객체 생성과 관련된 부분을 국지화 시킬 때 유용하다.

장점
1. 어떤 객체를 생성할 것인지와는 무관하게 동일한 형태로 프로그래밍이 가능하다. 이 것은 두개의 상위 클래스가 있기 때문에 가능하다.
2. 직접 생성자를 호출해서 객체를 생성하는 것보다 훨씬 유연하고 확장성 있는 구조이다.
3. 추후에 객체 생성방법이 바뀌더라도 Factory Method 만 수정하면 된다.

단점

1. 생성할 객체의 종류가 달라질때 마다 새로운 하위 클래스를 정의해야 한다. 이는 불필요하게 많은 클래스를 정의해야 한다는 문제점이 있다.

가장 간단한 예제는 다음과 같다.

class Product { ... };

class UserA {
public:
  void doSomething {
    Product p = new Product();
  }
};

class UserB {
public:
  void doSomethong {
    Product p = new Product();
  }
};

 위의 Code는 Product를 생성자를 호출함으로 직접 생성하고 있다. 위의 경우 만약 Product의 생성자가 매개변수를 가져야 하는 식으로 바뀔 경우 UserA, UserB 및 기타 생성자를 호출한 모든 Code를 다 수정해야 한다. 하지만 Factory Method를 이용하여 생성한 경우 해당 Method만 수정하면 되므로 유지보수성이 좋아진다.

class Product { ... };

class Factory {
public:
  static Product* getProduct {
    return new Product(); // 생성자에 변화가 생길 경우 여기만 수정하면 됨
  }
};

class UserA {
public:
  void doSomething {
    std::tr1::shared_ptr<Product> p(Factory::getProduct());
  }
};

class UserB {
public:
  void doSomethong {
    std::tr1::shared_ptr<Product> p(Factory::getProduct());
  }
};

 Factory Method를 사용하는 형식으로 고친 Code이다. 스마트 포인터 중 shared_ptr을 사용해서 혹시나 실수로 포인터를 지우지 않는 실수를 미연에 방지했다. 이 경우 생성자 쪽에 변화가 생길경우 Factory Method만을 수정하면 된다.

 조금 더 일반적인 예제를 들어보겠다.



 unit에는 marine 과 tank의 2가지 종류가 있고, 사용자 입장에서는 fire()라는 함수만을 이용하면 된다. 그리고 unit을 생성하기 위한 Factory로는 building 이라는 기본 클래스가 있고 그 아래 marine 과 tank의 생성을 담당하는 파생 클래스가 각각 존재한다.

#include "stdafx.h"
#include <iostream>
 
class unit{
public:
    enum{ marine = 0, sniper = 1, tank = 2, bike = 3 };
    virtual void fire() = 0;
};
class marine : public unit{
public:
    void fire() { std::cout << "marine Fire!\n"; }
};

class tank : public unit{
public:
    void fire() { std::cout << "tank Fire!\n"; }
};
 
class building{
public:
    unit* newUnit(int v){
        unit* pUnit = CreateUnit(v);
        return pUnit;
    }
protected:
    virtual unit* CreateUnit(int v) = 0;
};
 
class barrack : public building{
protected:
    unit* CreateUnit(int v) {
        if(v == unit::marine) return new marine;
        else  return NULL;
    }
};
 
class factory : public building{
protected:
    unit* CreateUnit(int v) {
        if(v == unit::tank)  return new tank;
        else  return NULL;
    }
};
 
int _tmain(int argc, _TCHAR* argv[]) {
    int input = unit::tank;
    unit* pUnit;   // 어떤 unit을 만들지 몰라 unit 포인터를 만들었습니다.
    if(input == unit::marine){
        barrack b;    // 배럭에서 유닛을 생성하기 위해 배럭을 하나 만들어 주었습니다.
        pUnit = b.newUnit(input);       // 입력값에 의해 마린 유닛을 만들었습니다.
    }
    else if (input == unit::tank){      // 전차를 만듭니다.
        factory f;                      // 공장을 만들어주고.
        pUnit = f.newUnit(input);       // 유닛을 만들었습니다.
    }
    else
        return 0;
     
    if(pUnit) pUnit->fire(); // 유닛이 무엇이던간에 일단 공격합니다.
 
    return 0;
}

참조 : http://devsw.tistory.com/61
         http://www.youtube.com/watch?v=ApwZxNOFOaI



댓글 없음:

댓글 쓰기