Al extender funcionalidad, trabajando sobre código existente, encontrarás partes que no son fáciles de extender puesto que sólo una parte de este es lo que varia pero es todo lo que se encapsula. En este caso es necesario una refactorización. Por favor, NO CORTAPEGUES sólo por que sera más rápido, con el tiempo y sucesivas extensiones, tu código habrá superado el umbral de la deuda técnica recuperable (otro tema de post). Un ejemplo:

class MyClass
{
    public function nextStep()
    {
        //puedo ir al paso siguiente
        if ($this->_currentPos == $this->_maxLength) {
            //doing something specific
            $this->_currentState = 2 * $this->_currentState + 4;
            $this->_currentPos ++;
        }
    }
}

En esta situación siempre se pueden optar por dos caminos:

  • Herencia
  • Composición

Dependiendo del problema sera mas aconsejable uno u otro pero casi siempre irás más seguro si compones. El mayor problema de la composición es que tendrás muchas más clases y por tanto tendrás que elegir bien donde ponerlas para que tenga sentido pero siempre será mas fácil reutilizar este código. Te diria que lo mas aconsejable es componer pero de verdad depende del problema a resolver. Si siempre se tratase de componer, la herencia no existiria en POO, no?!.

Refactoring Por Herencia.

Template Method Pattern

Para mi esta es la mejor forma de atacar un problema que se puede considerar pequeño, como una parte de un método que simplemente realiza un cálculo, etc. En el ejemplo de antes seria:

class MyClass
{
    public function nextStep()
    {
        //doing something that is always the same
        if ($this->_canIncrement()) {
            //doing something specific
            $this->_doNextStep();
            $this->_currentPos++;
        }
    }

    protected function _canIncrement()
    {
        return $this->_currentPos == $this->_maxLength;
    }

    protected function _doNextStep()
    {
        $this->_currentState = 2 * $this->_currentState + 4;
    }
}

Como ves aquí, hemos movido la parte que queríamos variar a un método (normálmente protected) que será llamado desde el método de entrada. Así, podremos luego por herencia modificar solo la parte que queríamos modificar.

class MyNewClass extends MyClass
{
    protected function _doNextStep()
    {
        $this->_currentState = ($this->_currentState + 5) / 2;
    }
}

Refactoring Por Composición

Factory Method Pattern 

El Factory Method Pattern suele implementarse como una especificación “creacional” del template method pattern así que también podemos usar esta técnica para componer llevando el desacoplamiento un paso mas alla. ¿Lo malo?, mas clases = mas archivos = mas “difícil de seguir”. No me mal interpretes, esto que tiene de “malo” no lo es tanto cuanto lo que ganas al desacoplar tu código. ¿Cuando usar esta técnica? Si al hacerlo repartes (correctamente) las responsabilidades de tu código.

class MyClass
{
    public function nextStep()
    {
        //doing something that is always the same
        if ($this->_canIncrement()) {
            //doing something specific.- Also Strategy Pattern here
            $stepper = $this->_createStepper();
            $this->_currentState = $stepper->stepUp($this->_currentState);
            $this->_currentPos ++;
        }
    }

    protected function _canIncrement()
    {
        return $this->_currentPos == $this->_maxLength;
    }

    protected function _createStepper()
    {
        return new MyStepperClass();
    }
}

class MyStepperClass
{
    protected function stepUp($currentValue)

    {
        return 2 * $currentValue + 4;
    }
}

//estas serian las clases de tu nueva implementación
class MyNewClass extends MyClass
{
    protected function _createStepper()

    {
        return new MyNewStepperClass();
    }
}

class MyNewStepperClass extends MyStepperClass
{
    protected function stepUp($currentValue)
    {
        return ( $currentValue + 5 ) / 2;
    }
}

Concluyendo, cuando te encuentres con un método que querrías reutilizar en parte, muchas veces te vendrá a la cabeza “heredar y sobrescribir el método entero”. Mi recomendación: gasta un poco mas de tiempo en hacerlo un poco mejor y, si es necesario, en repensar el diseño del componente. Si crees que no es tu responsabilidad hacerlo por lo menos avisa a quien consideres y documéntalo, a no ser que se trate de una libreria externa. Aprenderás en el camino y tu codebase no aumentara demasiado su deuda técnica.

Hay muchas mas y mejores formas de atacar esta situación, pero estos son los primeros pasos. Ayer heredabas y cortapegabas, hoy implementas el Template Method Pattern, mañana (musica de star wars)…

 

Leave a reply

 

Your email address will not be published.