Corrigindo uma grande desvantagem ao padrão decorador

Decidi há algum tempo, na refatoração de algum código de combate de jogo, tentar o padrão de decorador. Os combatentes podem ter várias habilidades passivas e também podem ser tipos diferentes de criaturas. Eu percebi que decorador me permite adicionar o comportamento em várias combinações em tempo de execução, então eu não preciso de centenas de subclasses.

Eu quase terminei de fazer os cerca de 15 decoradores das habilidades passivas, e nos testes descobri uma coisa - uma desvantagem bastante gritante para o padrão decorador que estou surpreso de não ter ouvido falar antes.

Para que os decoradores trabalhem, seus métodos devem ser chamados no decorador mais externo. Se a "classe base" - o objeto empacotado - chamar um de seus próprios métodos, esse método não será a sobrecarga decorada, já que não há como a chamada ser "virtualizada" para o wrapper. Todo o conceito de uma subclasse artificial é quebrado.

Isso é uma grande coisa. Meus combatentes têm métodos comoTakeHit que por sua vez chamam deDamage método. Mas o decoradoDamage não está sendo chamado de jeito nenhum.

Talvez eu tenha escolhido o padrão errado ou tenha excesso de zelo em sua aplicação. Você tem algum conselho sobre um padrão mais apropriado nesta situação, ou uma maneira de contornar essa falha? O código que eu refatorei só tinha todas as habilidades passivas espalhadas por todo o código de combate dentroif bloqueia em lugares aparentemente aleatórios, e é por isso que eu queria dividi-lo.

editar: algum código
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;
}

Este método está na baseCombatant classe.DamageReduction eDamage podem e são ambos substituídos em vários decoradores, por exemplo, um passivo que corta dano por um quarto, ou outro que reflete algum dano de volta ao atacante.

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;
    }
}

Mas, novamente, esses métodos de decorador nunca são chamados, porque não são chamados de fora, são chamados dentro da classe base.

questionAnswers(2)

yourAnswerToTheQuestion