ホーム < ゲームつくろー! < デザインパターン習得編
Decorator
〜知らずに着飾るオブジェクト
@ きらびやかな効果は後付けで
RPGでモンスターにダメージを与えたとき、ぴかっと光ったり、火花が出たり、揺れたり回ったりとエフェクトが楽しいですね。そういうエフェクトはいろいろ考えられるので、十分に一般化する必要があります。しかし、そういうエフェクトは「付け外し」が出来るとうれしいのです。
例えば、モンスターに毒攻撃を与えて紫色にし、さらに混乱魔法で混乱状態のマークが点滅した状態で、剣で切りつけると太刀筋が重なって光る!しばらくたつと「紫色が元に戻って」、混乱状態はそのまま。また太刀筋が光る。そのうち「混乱状態も元に戻り」、剣で切りつけると「ミスをしてミスマークが出る」。この短い間に、効果が付いたり消えたりしているのです。しかも、順番は任意と来ています。これを実装するにはどうしたらよいのでしょうか。
Decoratorパターンは、まさにそういった「後付けの効果」をうまく演出できるパターンです。
キャラクタークラスとその派生クラスであるモンスタークラスを定義します。
class Character
{
public:
virtual void Draw();
}
class Monster : public Character
{
protected:
int m_Code;
public:
virtual void Draw();
}
着飾らせたいのはMonsterクラスのオブジェクトです。そのために、Characterクラスの派生クラスとしてDecoratorクラスを次のように定義します。
class Decorator : public Character
{
protected:
Character* m_Inner; // 着飾らせたいオブジェクト
public:
virtual void Draw(){
m_Inner->Draw();
}
}
まず、着飾らせたいMonsterオブジェクトをコンポーネントとして登録します。さらに、Draw関数をオーバーライドしますが、ここでコンポーネントのDraw関数を呼び出します。結局、Decoratorクラスを通したオブジェクトは、「何の効果もなくモンスターがDrawされる」ことがわかります。
「?」ですね。何もしないクラスを作ってどうするんだと思ってしまいます。しかし、このクラスをさらに派生させたときに、素敵なことが起こります。
class FlashDecorator : public Decorator
{
public:
virtual void Draw{
m_Inner->Draw();
DrawFlash();
}
void DrawFlash(); // 光らせる!
}
m_Innerに着飾らせたいモンスターオブジェクトを登録し、Draw関数を呼び出すと、まずm_Innerに登録されたオブジェクトに実装されている描画が行われ、次にDrawFlash関数が呼び出されて絵が光る効果が「付け足されます」。
このDecoratorクラスの派生クラス群はすべて付け足しになるので、元のモンスターオブジェクトはこのクラスの事をまったく知りません。塵とも知らないのです。これがこのパターンのすばらしい所です。元のオブジェクトを作成する段階で、付け足し的な効果について気にする必要がまったく無くなります。しかも、別のDecoratorクラスを作成すれば、どんどん効果を重ねて行けるわけです。
Decoratorオブジェクトをまとめておけば、効果集のようなクラスが出来上がります。prototypeパターンと併用すると効果的ですね。
Decoratorパターンの関係式は次のようになります。
このままではよく分かりませんので、モンスターオブジェクトとそのDecoratorクラスに置き換えましょう。
Decoratorの派生クラスで追加する効果を定義し、登録したモンスターのDraw関数を呼び出すのがポイントです。
Decoraterは「重ね着」が可能などのようなオブジェクトにでも適用できます。これは「親クラスの肥大化」を防ぐ手段の1つとも言えましょう。継承の事を考えて親クラスに無用の機能まで盛り込んでしまうことは良くあります。大きすぎるクラスは、再利用性の低い物になってしまいますから、それは避けるべきなのです。このパターンはそれを防いでくれるわけです。