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

Builder
  〜同じ生成過程で完成する色々なオブジェクト


@ 作り方は同じで作られるものが違う


 例えば、RPGのキャラクタを作成する時、素っ裸のキャラクタに@下着を着せ、A鎧をまとわせ、B剣を持たせ、C兜をかぶせ、D盾を持たせ、E特殊能力を与えるとしましょう。この作成過程に従えば、主人公も、女戦士も、僧侶も、魔法使いもモンスターでさえも作成できます。

 作成過程は同じでも、材料が異なれば作成される物は異なる。また、材料が同じでも、作成過程が異なれば、少し違うものが出来る。1つのオブジェクトを生成するときに、「作成過程」と「材料」を分離すれば、そこに柔軟性が生まれます。

 特定の作成過程に従ってキャラクタを組み立てる「Director」と、キャラクタの材料(パーツ)の生成方法を知っている「Builder」を用いる事により、オブジェクト生成の柔軟性を高めたのがBuilderパターンです。ここでは、RPGのキャラクタオブジェクトの生成を例に、その動きを見ていく事にしましょう。

 まずBuilderとしてCharaBuilderクラスを作成します。

class CharaBuilder
{
protected:
   Character *m_Character;

pubic:
   virtual void SetUnderWear(int){};       // @下着を着せる
   virtual void SetArmor(int){};           // A 鎧をまとわせる
   virtual void SetSword(int){};            // B 剣を持たせる
   virtual void SetHelmet(int){};           // C 兜をかぶせる
   virtual void SetShield(int){};            // D 盾を持たせる
   virtual void SetSpecialAbility(int){};   // E 特殊能力を与える

   virtual Character* GetCharacter(){return NULL;};  // キャラクタを出力
};

@〜Eまではキャラクタを作り上げるために必要なインターフェイスです。しかし、このクラスでは、それぞれの関数が何も機能していない事に注目してください。Builderパターンではルートクラスでインターフェースのみを供給し、派生クラスで必要な作成方法を実装します。GetCharacter関数が最終的な出力になります。ここではRPGのキャラクタを表すCharacter型のオブジェクトへのポインタを返しています。

 このCharaBuilderクラスの派生クラスとして、魔法使いクラスWizerdBuilderクラスを作ってみます。

class WizardBuilder: pubilc CharaBuilder
{
pubic:
   void SetUnderWear(){m_Character->SetWear(ID_BLACKUNDER);}       // @下着を着せる
   void SetArmor(){m_Character->SetArmor(ID_BLACKROBE);}           // A 黒いローブをまとわせる
   void SetSword(){m_Character->SetSword(ID_STICK);}                     // B 杖を持たせる
   void SetHelmet(){m_Character->SetHelmet(ID_WZ_HAT);};                // C 魔法使いハットをかぶせる
                                                                                            // D 盾は持てない
   void SetSpecialAbility(){m_Character->SetAbility(ID_SPELL)};        // E 魔法を唱える能力を与える

   Character* GetCharacter(){return m_Character;}  // 魔法使いキャラクタを出力
};

このBuilderクラスでは、魔法使いキャラクタを生成する具体的な方法を実装しています(IDによる生成)。D番の盾は、魔法使いは持てないとしました。ですから、親クラスのSetShiled関数をオーバーライドする必要はありません。モンスターのBuildクラスでは、それ以外の部分についても必要ないかもしれませんが、それも関数をオーバーライドしなければ良いだけの話です。

次にBuilderクラスを扱うDirectorとしてCharaDirectorクラスを作成します。

class CharaDirector
{
   Character* Create(CharaBuilder* chara)
   {
         chara->SetUnderWear();       // @下着を着せる
         chara->SetArmor();           // A 鎧をまとわせる
         chara->SetSword();            // B 剣を持たせる
         chara->SetHelmet();           // C 兜をかぶせる
         chara->SetShield();            // D 盾を持たせる
         chara->SetSpecialAbility();   // E 特殊能力を与える

         return chara->GetCharacter;
   }
};


Directorクラスは最終的にCharacterオブジェクトを生成しますが、「どのキャラクターを生成しているのか」はわかっていません。Directorクラスは「生成手順」だけを知っているのです。

Builderパターンの模式図は次のようになります。


これをRPGのキャラクタを生成するBuilderパターンに照らし合わせると、


となります。


A こんなときはBuilder

 Builderパターンは、生成手順と材料を分離する事から、「生成方法が複雑なのでユーザーに代わってオブジェクトを作ってあげたい」時に利用できます。プラモデルで例えるなら、原料のプラスチックをユーザーが持っている状態です。がんばれば原型を作って、プラスチックを流し込んで、パーツを組み立ててプラモデルを完成できますが、それだとユーザーが大変過ぎます。ユーザーは「ガンプラ」が欲しいとDirectorに材料を与えれば、Directorは模型屋(Builder)に命令して各パーツを作ってもらい、最終的なガンプラの完成系をユーザーに与える事が出来ます。

 ゲーム製作では、固定的な生成過程でできるオブジェクトに対してBuilderパターンを適用できます。例えば、STGのステージ構成などは、だいたい固定しています。大まかなオブジェクトをBuilderパターンで作ってしまえば、新たなステージを追加するのも簡単です。