Corrigiendo una gran desventaja al patrón decorador.

Hace un tiempo decidí, al refactorizar un código de combate del juego, probar el patrón de decorador. Los combatientes pueden tener varias habilidades pasivas, y también pueden ser diferentes tipos de criaturas. Pensé que el decorador me permite agregar comportamiento en varias combinaciones en el tiempo de ejecución, por lo que no necesito cientos de subclases.

Casi termino de hacer los 15 o más decoradores para las habilidades pasivas, y al probar descubrí algo, una desventaja bastante evidente del patrón del decorador del que me sorprende que no haya oído hablar antes.

Para que los decoradores trabajen, sus métodos deben llamarse al decorador más externo. Si la "clase base", el objeto envuelto, llama a uno de sus propios métodos, ese método no será la sobrecarga decorada, ya que no hay forma de que la llamada se "virtualice" al envoltorio. Todo el concepto de una subclase artificial se descompone.

Esto es una gran cosa Mis combatientes tienen métodos comoTakeHit que a su vez llaman su propioDamage método. Pero el decoradoDamage no está siendo llamado en absoluto.

Tal vez he elegido el patrón equivocado o he sido demasiado entusiasta en su aplicación. ¿Tiene algún consejo sobre un patrón más apropiado en esta situación, o una forma de solucionar este defecto? El código que he refaccionado acaba de tener todas las habilidades pasivas esparcidas por todo el código de combate en el interiorif bloques en lugares aparentemente aleatorios, por eso quería romperlo.

edición: algo de 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á en la base.Combatant clase.DamageReduction yDamage pueden y están anulados en varios decoradores, por ejemplo, un pasivo que corta el daño en un cuarto, u otro que refleja algún daño al 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;
    }
}

Pero, nuevamente, estos métodos de decorador nunca son llamados, porque no se los llama desde el exterior, se les llama dentro de la clase base.

Respuestas a la pregunta(2)

Su respuesta a la pregunta