ホーム < ゲームつくろー! < デザインパターン習得編
Factory Method
〜子オブジェクトを親クラスの関数が作る
@ 何を生成するかわからない!
Factory Methodは多分知らずに使っているかもしれない、生成に関するデザインパターンの中では簡単な部類に入るパターンです。
ゲームには沢山のアイテムがあります。RPGでは特に膨大なアイテムが提供されます。そのアイテムは、プレーヤーがお店で買うかもしれませんし、モンスターが落としていくかもしれませんし、宝箱の中に入っているかもしれません。いずれにせよ、それを手に入れるタイミングはプレーヤー次第です。
例として、「Store」および「Item」というクラスを作ります。
class Store
{
public:
virtual Item* GetItem(int item_id) = 0;
};
class Item
{
protected:
string m_name;
};
StoreクラスのGetItem関数は、引数のアイテムIDに従って、何かアイテムを取り出すことができます。しかし、何のアイテムを取り出せるかはお店によって異なるので、このクラスで実装はできません。そのため、この関数は純粋仮想関数になっています。
Item関数は、ここでは簡単のためその名前を保持しているとします。
storeクラスを派生させて具体的な武器屋クラスの実装を行います。
class WeaponStore : public Store
{
public:
Item* GetItem(int item_id){
switch(item_id){
case LONG_SWORD:
return new LongSword(); // 長剣を生成
break;
case SHORT_SWORD:
return new ShortSword(); //短径を生成
break;
default:
return NULL; // 生成物なし
}
return NULL;
};
この関数は、引数の剣アイテムのIDに従い、Itemクラスの派生オブジェクトを生成します。実は、こういった生成関数を定義する事、これが「Factory Method」です。簡単ですね。
A Factory Methodの利点と欠点
Factory Methodの利点はどこにあるでしょうか?
例えば、ある町には2つのお店があるとします。この時、町クラスはStoreクラスのポインタを2つ(配列として)保持します。
class Town } protected: vector<Store*> m_StoreAry; }; |
Townクラスは自分が持っているお店が何であるかは知りませんが、StoreクラスのGetItem関数を呼び出す事はできます。ここで思い出して欲しいのが、Storeクラスは「抽象クラス」です。よって、GetItemクラスで取り出すことにできるアイテムは、Townオブジェクトに渡されたStoreクラスの派生オブジェクト(WeaponStore, ToolStore, etc...)によって変化できます。大変柔軟な設計になっているのです。
Factory Methodは、「生成」という部分を具体的にしないで、派生可能な関数やクラスによって初めて具体的にする事により、生成物に柔軟性を与えるデザインパターンです。その構造図は次のようになります。
先ほどの「Store」と「Item」クラスに置き換えると次のようになります。
このパターンはありとあらゆる生成部分で活用できます。しかし、「クラスの数が多くなる」「新しい生成物(CopperSword)を作るには生成者を派生(StrongWeaponStore)させなければならい」という欠点も持っています。これは、Factory
Methodを多用すると、クラス管理が大変になることを示しています。この問題は、「prototypeパターン」で大きく改善されます。
このパターンによってオブジェクトの生成を別にしておく事は、オブジェクトの変更や総入れ替えを可能にしてくれますから、使い所を見極めてうまく利用したいものです。