少し前に、ゲームの戦闘コードをリファクタリングする際に、デコレータ パターンを試すことにしました。戦闘員はさまざまな受動的能力を持つことができ、さまざまな種類のクリーチャーである可能性もあります。デコレーターを使用すると、実行時にさまざまな組み合わせで動作を追加できるので、何百ものサブクラスは必要ないと考えました。
パッシブ アビリティ用の 15 個ほどのデコレータの作成がほぼ完了しました。テスト中に、これまで聞いたことのないデコレータ パターンの明らかな欠点を発見しました。
デコレーターが機能するには、最も外側のデコレーターでメソッドを呼び出す必要があります。「基本クラス」 (ラップされたオブジェクト) が独自のメソッドの 1 つを呼び出す場合、呼び出しをラッパーに「仮想化」する方法がないため、そのメソッドは装飾されたオーバーロードにはなりません。人工サブクラスの概念全体が崩壊します。
これは大したことです。私の戦闘員はTakeHit
、自分のDamage
メソッドを順番に呼び出すようなメソッドを持っています。しかし、装飾されたDamage
ものはまったく呼び出されません。
おそらく、間違ったパターンを選択したか、その適用に熱心すぎたのでしょう。この状況でのより適切なパターン、またはこの欠陥を回避する方法について何かアドバイスはありますか? 私がリファクタリングしたコードは、一見ランダムな場所のブロック内の戦闘コード全体にすべての受動的能力が散らばっていたif
ので、それを分解したかったのです.
編集:いくつかのコード
public function TakeHit($attacker, $quality, $damage)
{
$damage -= $this->DamageReduction($damage);
$damage = round($damage);
if ($damage < 1) $damage = 1;
$this->Damage($damage);
if ($damage > 0)
{
$this->wasHit = true;
}
return $damage;
}
このメソッドは基本Combatant
クラスにあります。DamageReduction
さまざまなデコレーターでDamage
オーバーライドできます。たとえば、ダメージを 4 分の 1 にカットするパッシブや、ダメージを攻撃者に反射するパッシブなどです。
class Logic_Combatant_Metal extends Logic_Combatant_Decorator
{
public function TakeHit($attacker, $quality, $damage)
{
$actual = parent::TakeHit($attacker, $quality, $damage);
$reflect = $this->MetalReflect($actual);
if ($reflect > 0)
{
Data_Combat_Event::Create(Data_Combat_Event::METAL_REFLECT, $target->ID(), $attacker->ID(), $reflect);
$attacker->Damage($reflect);
}
return $actual;
}
private function MetalReflect($damage)
{
$reflect = $damage * ((($this->Attunement() / 100) * (METAL_REFLECT_MAX - METAL_REFLECT_MIN)) + METAL_REFLECT_MIN);
$reflect = ceil($reflect);
return $reflect;
}
}
しかし、繰り返しになりますが、これらのデコレータ メソッドは、外部から呼び出されるのではなく、基底クラス内で呼び出されるため、呼び出されることはありません。