デザインパータンの一種である彼は、
ただ、 new してオブジェクトを返す。
class Human{};
;// 人間を作る工場
class HumanFactory
{
public:
Human* CreateInstance()
{
return new Human;
}
};
~~~~~~~~~~~~~~~~~~
HumanFactory* pHumanFactory = new HumanFactory;
// Humanが生成される
Human* pHuman = pHumanFactory->CreateInstance();
;// 人間を作る工場
class HumanFactory
{
public:
Human* CreateInstance()
{
return new Human;
}
};
~~~~~~~~~~~~~~~~~~
HumanFactory* pHumanFactory = new HumanFactory;
// Humanが生成される
Human* pHuman = pHumanFactory->CreateInstance();
↑ Human が new されて戻ってくる。
そして上の例では delete を忘れてしまいそうなので本当はスマートポインタにしたい所。
『なんだそれ??
生成とか自分で new 書いてするし特にいらねーじゃん・・・』
と、ずっと思って使ってなかったのですが、
『おぉ~!』と思える例がありました。
********************************************
① : 生成コードを分ける。
ゲーム作成時、ある所でモンスターを作りたいとなりました。
識別子によって生成したいモンスターを変えたいです。
現在、モンスター情報クラス自体が生成コードを持つ書き方をしています ↓
// モンスター情報を持つ
class MonsterInfo
{
private:
Monster* pMonster;
public:
Monster( MONSTER_TYPE )
{
switch( MONSTER_TYPE )
{
case SLIME: pMonster = new Slime;
・
・
・
}
}
};
class MonsterInfo
{
private:
Monster* pMonster;
public:
Monster( MONSTER_TYPE )
{
switch( MONSTER_TYPE )
{
case SLIME: pMonster = new Slime;
・
・
・
}
}
};
う~ん・・・
あまり長いコードを1つのクラスに書くのは可読性を殺してしまう・・・
出来れば生成コードは別の所に移したい。。
それにモンスター生成コードだけは別の場所でも使うかも。
そこでまた書くの面倒くっっさ Σ(゚ё゚ノ)ノ
って時にFactoryクラスを別で作成し、この中でガッシャンガッシャン作っちまいます。
//** モンスターを作成する工場
class MonsterFactory
{
public:
Monster* CreateInstance( MONSTER_TYPE type )
{
Monster* pMonster;
switch( type )
{
case FASHION_MONSTER: pMonster = new FashionMonster;
case OPPAI_MONSTER: pMonster = new OppaiMonster;
};
return pMonster;
}
};
class MonsterFactory
{
public:
Monster* CreateInstance( MONSTER_TYPE type )
{
Monster* pMonster;
switch( type )
{
case FASHION_MONSTER: pMonster = new FashionMonster;
case OPPAI_MONSTER: pMonster = new OppaiMonster;
};
return pMonster;
}
};
なんか、
人間やモンスターを工場で生成するって思うと悲しくなりますねw
これでモンスターの生成を分けることが出来、
ファクトリーに識別子を渡すだけでモンスターが返ってきます。
MonsterFactory* pMonsterFactory = new MonsterFactory;
Monster* pMonster = pMonsterFactory->CreateInstance( FASION_MONSTER );
Monster* pMonster = pMonsterFactory->CreateInstance( FASION_MONSTER );
↑のファクトリーは、
様々なクラスに使用する可能性もあり、使い回しできそうなので
シングルトン化するといいかもしれないです。
② : 色々変化するぜ!
class Human{};
という 人間の元クラスがあり、こいつを継承しているクラスがたくさんあるとします。
識別子を使わず、生成をその場その場で切り替えたい時。
class Human{};
class Woman : public Human {};
class Gentleman : public Human {};
class HumanFactory
{
public:
virtual Human* CreateInstance() = 0;
};
class Woman : public Human {};
class Gentleman : public Human {};
class HumanFactory
{
public:
virtual Human* CreateInstance() = 0;
};
という基底ファクトリークラスを作って、
Class WomanFactory : public HumanFactory
{
public:
virtual Human* CreateInstance() { return new Woman; }
};
Class GentlemanFactory : public HumanFactory
{
public:
virtual Human* CreateInstance() { return new Gentleman; }
};
・
・
・
~~~~~~~~~~~~~~~~~~~~~~~~
HumanFactory* pHumanFactory = new pWomanFactory();
// 女性が生成される(*´Д`)
Human* pHuman;
pHuman = pHumanFactory->CreateInstance();
{
public:
virtual Human* CreateInstance() { return new Woman; }
};
Class GentlemanFactory : public HumanFactory
{
public:
virtual Human* CreateInstance() { return new Gentleman; }
};
・
・
・
~~~~~~~~~~~~~~~~~~~~~~~~
HumanFactory* pHumanFactory = new pWomanFactory();
// 女性が生成される(*´Д`)
Human* pHuman;
pHuman = pHumanFactory->CreateInstance();
識別子がないので良い感じ///
しかし、型によってこんなの作っていては使いにくい。 いや、面倒くさい
③ : 柔軟なFactory
型によって作成するのが面倒臭いなら型をなくせばいいじゃ~ん。
そういうことで template を使ってみます。
template < class T >
class Factory
{
public:
virtual T* CreateInstance() { return new T; }
};
~~~~~~~~~~~~~~
Factory< Woman > pHumanFactory;
Human* pHuman = pHumanFactory.CreateInstance();
↑ Woman インスタンスが作成される。
class Factory
{
public:
virtual T* CreateInstance() { return new T; }
};
~~~~~~~~~~~~~~
Factory< Woman > pHumanFactory;
Human* pHuman = pHumanFactory.CreateInstance();
↑ Woman インスタンスが作成される。
これで大分Factoryが使いやすくなりました。
④ : 変身
次に、
メタモンがいます。
・・・って、まぁ実際にはいないし、特に意味もないんですがw
何でも変身できるメタモン。
Object というクラスを継承したクラスに何でもなれるとします。
実際は Objectクラスを内部にもち、使用出来る。。とする。
コンストラクタに変身先の識別子を渡すようにした場合、
class Metamon
{
private:
OBJECT_TYPE mObjectType;
Object* mpObject;
public:
Metamon( OBJECT_TYPE type )
: mObjectType( type )
{
switch( type )
{
case TYPE_NAKED_LADY: mpObject = new NakedLady;
case TYPE_OPPAI: mpObject = new Oppai;
・
・
・
}
}
}
まぁ、臭い。
そして、後から新たな変身先を追加した場合、
Metamonクラスをいじらなければなりません。
しかし、Metamonクラスをライブラリ化していた場合
Metamonクラスをいじることは不可能になります。
これでは新しく何かを追加したい場合、恐らく現実から逃走します。
なので、
ファクトリークラスを渡すようにします。
class Metamon
{
private:
Object* mpObject;
// Object型のFACTORYクラスを内部に持つ
Factory< Object > mObjectFactory;
public:
Metamon( Factory< Object >& objectFactory )
: mObjectFactory( objectFactory )
{
// 例ではコンストラクタで生成してるが、
// いろいろな場所で生成できる。
mpObject = objectFactory.CreateInstance();
}
};
{
private:
Object* mpObject;
// Object型のFACTORYクラスを内部に持つ
Factory< Object > mObjectFactory;
public:
Metamon( Factory< Object >& objectFactory )
: mObjectFactory( objectFactory )
{
// 例ではコンストラクタで生成してるが、
// いろいろな場所で生成できる。
mpObject = objectFactory.CreateInstance();
}
};
(こんな作りをする人がいるかわかりませんがw)
コレだと自分でファクトリークラスを色々書きなおしてから
渡すだけでそれに変身してくれるので良い感じ、じゃないでしょうか?
Metamonがライブラリ化していても変身先を増やせる。
まぁ、こんな感じに作りたいと思った場合、
コンストラクタで Object を継承したクラスのtemplateファクトリークラスを渡したいです。
// Objectを継承したクラス。
class Oppai : public Object
{};
// Oppaiに変身して欲しいからOppai型のFactoryを作成
Factory< Oppai > objectFactory;
// こんなかんじに渡すのかな?
Metamon* pMetamon = new Metamon( objectFactory );
{};
// Oppaiに変身して欲しいからOppai型のFactoryを作成
Factory< Oppai > objectFactory;
// こんなかんじに渡すのかな?
Metamon* pMetamon = new Metamon( objectFactory );
しかし、これはエラーが出ます。
Oppaiが Object を継承しているとはいえ、
Factory< Oppai > と Factory< Object > の間にはなんの継承関係もないのでダメです。
なので新たなファクトリクラス。。
// T が基底クラス。 S は Tを継承したクラス。
template < class T, class S >
class SecondFactory : Factory< T > // Factory< T >を継承している
{
public:
// SをTに変換して返す
virtual T* CreateInstance() { return new S; }
};
template < class T, class S >
class SecondFactory : Factory< T > // Factory< T >を継承している
{
public:
// SをTに変換して返す
virtual T* CreateInstance() { return new S; }
};
を作ってみる。
こうすれば、
// Object と、それを継承した Oppai を template に渡す
SecondFactory < Object, Oppai > objectFactory;
// Oppaiに変身するメタモン
Metamon* pMetamon = new Metamon( objectFactory );
// Oppaiに変身するメタモン
Metamon* pMetamon = new Metamon( objectFactory );
と、
引数のファクトリークラス( SecondFactory< T, S >)とメタモン内にあるファクトリークラス( Factory< T > )は
継承関係にあるのでエラーは出ません。
かつ、 Metamon内で ファクトリーを使うと Oppai クラスが生成されます。
なんか当たり前のようですが、ファクトリーの有用性がわかった気がしました。
こんなかんじに、
後から、生成したいクラスを増やす or 変えたい!
という時にすごい便利です。
たとえば、
上記に加えてメタモンにお金にも変身して欲しい時、
Factory クラスを新たに
class Money : public Object
{};
そして 新たなFactory...
SecondFactory < Object, Money > objectFactory;
と作れば、{};
そして 新たなFactory...
SecondFactory < Object, Money > objectFactory;
~~~~~~~~~~~~~~
// お金に変身するメタモン
Metamon* pMetamon = new Metamon( objectFactory );
Metamon* pMetamon = new Metamon( objectFactory );
メタモンはお金に変身します。
お金ほしいわー・・・
(´;ω;`)
*******************************
これまで、Factoryを使って来なかったので、コレを踏まえて僕も
Factoryをまな板の鯉のように、踊るように使ってゲームを作成してみたいです( - ω - )
↑の SeconFactory は、もっと使いやすく出来ます。
一々コンストラクタに引数で渡してるあたりを消して、、、
デフォルトでは Oppai を生成ようにする。 指定があれば生成するObjectを変化させる。
という風にしたいです( ゚ρ゚ )
Windowsプロフェッショナルプログラミングという本に
完全実装が載っております。
C++ を初めて『そろそろ新しいこと覚えたいなぁ~・・・』
となってる方に是非進めたい一品でございまする。
中古ではものすっごやすいですw
中古といえど、僕のは新品同様でした。CDも未開封でしたし..
- Windowsプロフェッショナルゲームプログラミング/秀和システム
- ¥2,940
- Amazon.co.jp
以上!
仕事頑張る