Исправление большого недостатка шаблона декоратора

Некоторое время назад при рефакторинге боевого кода игры я решил попробовать шаблон декоратора. У бойцов могут быть различные пассивные способности, а также они могут быть разными типами существ. Я понял, что декоратор позволяет добавлять поведение в различных комбинациях во время выполнения, поэтому мне не нужны сотни подклассов.

Я почти закончил делать 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;
    }
}

Но опять же, эти методы декоратора никогда не вызываются, потому что они вызываются не извне, а внутри базового класса.

6
задан Tesserex 19 August 2012 в 20:10
поделиться