ホーム < ゲームつくろー! < デザインパターン習得編
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パターンで作ってしまえば、新たなステージを追加するのも簡単です。