Исправление большого недостатка шаблона декоратора
Некоторое время назад при рефакторинге некоторого боевого кода игры я решил попробовать шаблон декоратора. У бойцов могут быть различные пассивные способности, и они также могут быть разными типами существ. Я подумал, что декоратор позволяет мне добавлять поведение в различных комбинациях во время выполнения, поэтому мне не нужны сотни подклассов.
Я почти закончил делать примерно 15 декораторов для пассивных способностей, и в ходе тестирования я обнаружил кое-что - довольно явный недостаток шаблона декоратора, о котором я удивился, о котором раньше не слышал.
Чтобы декораторы работали вообще, их методы должны быть вызваны на самом внешнем декораторе. Если «базовый класс» - обернутый объект - вызывает один из своих собственных методов, этот метод не будет декорированной перегрузкой, поскольку нет никакого способа для вызова быть "виртуализированным" для вызова; к обертке. Вся концепция искусственного подкласса рушится.
Это своего рода большое дело. У моих бойцов есть такие методы, как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
могут и могут быть переопределены в различных декораторах, например пассив, который уменьшает урон на четверть, или другой, который отражает некоторый урон атакующему.
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;
}
}
Но опять же, эти методы декоратора никогда не вызываются, потому что они не вызываются извне, они вызываются внутри базового класса.