epelpad

Como programar como los machos: Patrón Strategy

este post no está destinado para los programadores que empiezan a aprender un lenguaje de programación. Para sacar el mejor provecho, se debe de cumplir los siguientes requisitos:
-Saber un lenguaje de programación orientado a objetos (no tiene que saber todas las clases, librerías que pueda contener)
-Saber lo básico de UML
-Por lo menos aber lo que es un objeto, herencia, polimorfismo y encapsulación a nivel de teoría.
-Tener ganas de aprender

Como programar como los machos: Patrón Strategy


Normalmente en un tutorial, te ponen ejemplos muy básicos, y nada que se acerca al mundo real del negocio de la programación.
Vamos a aprender el patrón de diseño: Strategy Pattern
¿Para qué nos sirve?(En cristiano):
-Para tener código de más calidad
-Para que cuando programes hoy, y tengas que modificar algo, no tengas que modificar todo lo que hiciste, sino una porción del código. (Flexibilidad)
-Para cuando tengas que agregar una funcionalidad, no tengas (o sea muy poco) que modificar lo que ya está hecho y funciona (Extensibilidad).
Empecemos con un ejemplo del mundo real: Facturación, tenemos una clase Factura y poniendo un ejemplo, en cada estado tenemos un impuesto diferente. En San Petersburgo el impuesto calculo a 16%, en Santa Clarita el impuesto está a 18%, en Mordor el impuesto está a 10%.
¿Qué solución podemos tener a este problema?
Bueno, siguiendo la corriente a la modalidad orientada a objetos, entonces crearíamos una clase abstracta Factura, ¿y crear un tipo de factura para cada estado no? Veamos
[color=#000000]
public abstract class Factura {
    float totalFactura;
    float totalImpuestos;
    public abstract void calcularImpuestos();
    public void calcularTotalFactura() {
        totalFactura += totalImpuestos;
    }
    public float getTotalFactura(){
        return totalFactura;
    }
    public float getImpuestos(){
        return totalImpuestos;
    }
}



public class SanPetersburgoFactura extends Factura {
    public SanPetersburgoFactura(float totalParam) {
        totalFactura = totalParam;
    }
    @Override
    public void calcularImpuestos() {
        totalImpuestos = (float) (totalFactura * 0.16);
    }

}

public class SantaClaritaFactura extends Factura {
    public SantaClaritaFactura (float totalParam) {
        totalFactura = totalParam;
    }

    @Override
    public void calcularImpuestos() {
        totalImpuestos = (float) (totalFactura * 0.18);
    }

}
public class MordorFactura extends Factura {
    public MordorFactura (float totalParam) {
        totalFactura = totalParam;
    }

    @Override
    public void calcularImpuestos() {
        totalImpuestos = (float) (totalFactura * 0.10);
    }

}

[/color]

¡¡¡ Excelente!!! ¡¡¡ Un genio del diseño!!!!! ¡¡¡No!!!! ¿Y si hay que agregar otra ciudad que tenga otro impuesto? “Pues fácil, creamos otra clase que herede de Factura y listo”. ¿Y si el calculo del total de esa nueva factura necesita que se agreguen unos cargos o algo así? “Fácil, creamos un método que lo modifique en la nueva clase”. ¿Y si dos estados tienen el mismo impuesto, pero diferentes cargos extras? “Ah, me agarraste ahí. Ah espera, heredo de uno que ya tenga el impuesto similar y agrego el método de cargos nuevo!!”. ¡No!. Estás modificando más de lo que deberías de hacer. ¿Por qué no mejor utilizar los PATRONES DE DISEÑO, que existen para resolvernos esos dolores de cabeza?.
Vamonos con el patrón de diseño: Strategy Pattern. Lo que enseña este patrón es que, lo que cambia constantemente (en este caso, el cálculo de impuestos) lo separemos de lo que no cambia y lo transformemos en funcionalidades. Ahora trabajemos con los ejemplos:
Creamos la interfaz de la que implementarán los calculadores de impuestos por cada estado:
[color=#000000]

public interface IImpuestos {
    public double CalcularImpuestos(double cantidadParam);
}

[/color]

Y luego hacemos las clases que implementarán la interfaz creada
[color=#000000]

public class ImpuestosMordor implements IImpuestos {
    @Override
    public double CalcularImpuestos(double cantidadParam) {
        return cantidadParam * 0.10;
    }
}


public class ImpuestosSanPetersburgo implements IImpuestos{

    @Override
    public double CalcularImpuestos(double cantidadParam) {
        return cantidadParam * 0.16;
    }
}



public class ImpuestosSantaClarita  implements IImpuestos{

    @Override
    public double CalcularImpuestos(double cantidadParam) {
        return cantidadParam * 0.18;
    }

}

[/color]

Con esto, se evita crear una factura diferente cada vez que haya un calculo de impuesto nuevo y solamente se crean los calculadores. Pero para que funcione todo esto bien, tenemos que adecuar la clase factura, aquí esta:

[color=#000000]
public class Factura {
    double totalFactura;
    double totalImpuestos;
    IImpuestos calculadorImpuestos;
    public Factura(double total){
        totalFactura = total;
    }
    public void setCalculadorImpuestos(IImpuestos imp){
        calculadorImpuestos = imp;
    }
    public void calcularImpuestos(){
        totalImpuestos = calculadorImpuestos.CalcularImpuestos(totalFactura);
    }
    public void calcularTotalFactura() {
        totalFactura += totalImpuestos;
    }
    public double getTotalFactura(){
        return totalFactura;
    }
    public double getImpuestos(){
        return totalImpuestos;
    }
}

[/color]


WTF???? Pero hermano, has duplicado el código que tenía la factura.
Papucho, obviando algunos detalles que se pudieron haber hecho de otra forma, se ha resuelto un problema y se ha hecho la clase más flexible y extensible, los programadores van a hacer cola para petearte.
Ahora solamente crear una factura, y le asignas los cálculos de impuestos de forma dinámica en tiempo de ejecución:
[color=#000000]

public class Main {
public static void main(String[] args){
    Factura factura = new Factura(999);
    factura.setCalculadorImpuestos(new ImpuestosSanPetersburgo());
    factura.calcularImpuestos();
    factura.calcularTotalFactura();
    double totalCalculado = factura.getTotalFactura();
    System.out.println(totalCalculado);
}
}

[/color]

Esta es la parte mágica:
[color=#000000] 
    factura.setCalculadorImpuestos(new ImpuestosSanPetersburgo());

[/color]

Ahí puedes asignarle en tiempo de ejecución cualquier calculador de impuestos!!! Incluso se puede mejorar para que en vez de asignarle el calculador por código, se asigne REALMENTE de forma dinámica, supongamos que tenemos un método que es llamado cuando se elige el nombre de un calculador de un comboBox, entonces se asignaría así:
[color=#000000]

    factura.setCalculadorImpuestos( obtenerCalculadorDelComboBox());
[/color]

Y ya!!! Un diseño que salvo por algunos detalles (para simplificar el tutorial), es flexible.
Salu2

0 comentarios - Como programar como los machos: Patrón Strategy