code style

4/13/2007

Factory Method

Objective

定義生成物件的介面,但是讓子類別決定該具現哪個類別的物件。工廠方法將類別具現化交給子類別去處理。

Motivation

level designer利用level editor編輯了一個精心製作的關卡,其中有各式各樣的怪物配置在各處。level editor會將這些關卡資訊輸出成一個特定格式的檔案。在遊戲中,programmer將會讀取這些資訊並生成出正確的怪物類別。

由於該具現哪一類別的怪物將會隨著關卡設定而異,所以程式無法事先預測生成哪些怪物類別--程式只知道何時該生成怪物,而不知道該具現那一類別的怪物。解決方法就是使用Factory Method「將類別具現化交給子類別去處理」。



Implementation

class Creator {
public:
virtual Product* Create(int type);
};

Product* Creator::Create(int type) {
switch(type) {
case Type1: return new MyProduct1;
case Type2: return new MyProduct2;
case Type3: return new MyProduct3;
//...
case TypeN: return new MyProductN;
}
return NULL;
}


Problem

上述 pseudo code 雖然可以正常運作,但是卻非常難以擴展。譬如,我們需要在遊戲中加入新的怪物類別,所以不得不修改Creator::Create()、添加相對應的 case、原始碼中也需要include該怪物類別的header file。要怎麼修改才可以讓Factory Method任意添加新的類別而不用更動原本的原始碼呢?


Solution

透過template,我們可以自動產生每一個product的creator。然後再利用register的方式將creator註冊至factory method。那麼factroy method便可以找出對應的creator來具現所需要的類別。


class Factory {
public:
Object* Create(int type) { return mCreatorMap[type]->Create(); }
void Register(int type, const Creator* pCreator) { mCreatorMap[type] = pCreator; }
static Factory* GetInstance() { return mpInstance; }
private:
Factory() {}
typedef std::map CreatorMap;
static CreatorMap mCreatorMap;
static Factory *mpInstance;
};

Factory* Factory::mpInstance = new Factory;
Factory::CreatorMap Factory::mCreatorMap;


為了讓creator方便地註冊至factory method。我們可將factory method實做成Singleton模式。結合Singleton模式,我們可以利用靜態變數與物件建構式,將creator在程式初始化時便自動地註冊至factory method。


class Creator {
public:
virtual ~Creator() {}
virtual Object* Create() const = 0;
};

template
class ConcreteCreator : public Creator {
public:
ConcreteCreator() { Factory::GetInstance()->Register(T::_Type(), this); }
Object* Create() const { return new T; }
private:
static ConcreteCreator mRegister;
};


當我們需要添加一個新的怪物類別時,我們只需要實做出新的類別,並且加入下列代碼:

ConcreteCreator ConcreteCreator::mRegister;

That's all. :)

2 則留言:

匿名 提到...
網誌管理員已經移除這則留言。
匿名 提到...
網誌管理員已經移除這則留言。