ホーム < ゲームつくろー! < デザインパターン習得編

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パターン」で大きく改善されます。

 このパターンによってオブジェクトの生成を別にしておく事は、オブジェクトの変更や総入れ替えを可能にしてくれますから、使い所を見極めてうまく利用したいものです。