定義生成物件的介面,但是讓子類別決定該具現哪個類別的物件。工廠方法將類別具現化交給子類別去處理。
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::mapCreatorMap;
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 ConcreteCreatormRegister;
};
當我們需要添加一個新的怪物類別時,我們只需要實做出新的類別,並且加入下列代碼:
ConcreteCreator
That's all. :)
2 則留言:
張貼留言