Patrones de Diseño Creacionales

Un patrón de diseño es una estructura general que puede ser usada para solucionar un problema. No se trata ni de una librería ni de un esqueleto. Para que una solución se considere un patrón de diseño debe ser efectiva y reutilizable, es decir, que pueda aplicarse y resolver problemas similares.

Los Patrones Creacionales son los que proporcionan maneras de crear objetos ocultando la lógica de creación en lugar de utilizando el operador new.

Factory Method (Método fábrica)

Este patrón de diseño lo que recomienda es algo bien sencillo, en lugar de utilizar a lo largo del código el operador new para crear objetos, llamar a un método que se encargue de crearlos. De ahí su nombre.

El motivo es sencillo, imagina que tienes una aplicación de gestión de envíos y sólo usas la clase Camion, el día que la empresa crezca y haya que meter la opción Barco, tendrás que hacer cambios por todo el código, mientras que usando este patrón solo tendrás que modificar un método.

Utiliza este patrón cuando no tengas claros los objetos con los que trabajará tu aplicación. O cuando quieras proporcionar a tus objetos la posibilidad de ser extendidos.
interface Vehicle {
    void transport();
}

class Truck implements Vehicle {
    public void transport() {
        System.out.println("Truck transporting");
    }
}

class Ship implements Vehicle {
    public void transport() {
        System.out.println("Ship transporting");
    }
}

public class VehicleFactory {
    enum VehicleType {
        TRUCK,
        SHIP
    }

    public Vehicle getVehicle(VehicleType type) {
        switch (type) {
            case TRUCK:
                return new Truck();
            case SHIP:
                return new Ship();
        }
        return null;
    }
}
Abstract Factory (Fábrica abstracta)

Considerando el patrón anterior, este es una ampliación del mismo cuando el problema requiere de alteraciones de los productos (se llaman “productos” a los objetos creados por el Factory). Un punto negativo de este patrón es que el código se vuelve más complejo, aunque por otro lado es más flexible. Continuando con el ejemplo anterior, el Camion puede ser pequeño, frigorífico o incluso un tráiler.

Puedes pensar en utilizar el patrón anterior y considerar cada tipo de camión como un nuevo tipo de vehículo. Y aunque sería una solución, la clave está en que hablamos de productos con métodos y atributos muy similares, por lo que estaríamos duplicando código.

Al igual que en el patrón anterior la recomendación aquí es crear una interfaz por cada producto, y hacer el Factory abstracto.

Utiliza este patrón en las mismas circunstancias del patrón anterior pero cuando tu código vaya a funcionar con distintas familias de esos objetos.



Builder (Constructor)

Este patrón sigue la máxima informática de “divide y vencerás”. Cuando nos encontramos con objetos muy complejos de crear, para evitar tener un constructor gigantesco e ilegible, pero ojo, también para poder crear muchos objetos diferentes compuesto de diferentes partes.

La idea de este patrón es sacar el código del constructor del objeto y dividirlo en nuevos objetos independientes llamados “constructores”.

Aunque es opción, es recomendado tener una clase Director, que se encarga de llamar a todos los pasos. En el ejemplo se puede entender fácil:
/** "Producto" */
class Pizza {
    private String masa;
    private String salsa;
    private String relleno;

    public void setMasa(String masa)     { this.masa = masa; }
    public void setSalsa(String salsa)     { this.salsa = salsa; }
    public void setRelleno(String relleno) { this.relleno = relleno; }
}

/** "Abstract Builder" */
abstract class PizzaBuilder {
    protected Pizza pizza;

    public Pizza getPizza() { return pizza; }

    public abstract void buildMasa();
    public abstract void buildSalsa();
    public abstract void buildRelleno();
}

/** "ConcreteBuilder" */
class HawaiPizzaBuilder extends PizzaBuilder {
    public HawaiPizzaBuilder(){super.pizza = new Pizza();}
    public void buildMasa()   { pizza.setMasa("suave"); }
    public void buildSalsa()   { pizza.setSalsa("dulce"); }
    public void buildRelleno() { pizza.setRelleno("chorizo+alcachofas"); }
}

/** "ConcreteBuilder" */
class PicantePizzaBuilder extends PizzaBuilder {
    public PicantePizzaBuilder(){super.pizza = new Pizza();}
    public void buildMasa()   { pizza.setMasa("cocido"); }
    public void buildSalsa()   { pizza.setSalsa("picante"); }
    public void buildRelleno() { pizza.setRelleno("pimienta+salchichón"); }
}

/** "Director" */
class Cocina {
    private PizzaBuilder pizzaBuilder;

    public void setPizzaBuilder(PizzaBuilder pb) { pizzaBuilder = pb; }
    public Pizza getPizza() { return pizzaBuilder.getPizza(); }

    public void construirPizza() {
        pizzaBuilder.buildMasa();
        pizzaBuilder.buildSalsa();
        pizzaBuilder.buildRelleno();
    }
}

/** Un cliente pidiendo una pizza. */
class BuilderExample {
    public static void main(String[] args) {
        Cocina cocina = new Cocina();
        PizzaBuilder hawai_pizzabuilder = new HawaiPizzaBuilder();
        PizzaBuilder picante_pizzabuilder = new PicantePizzaBuilder();

        cocina.setPizzaBuilder( hawai_pizzabuilder );
        cocina.construirPizza();

        Pizza pizza = cocina.getPizza();
    }
}

/**
 *  2da opción para el abstract builder quizá más transparente para su uso.
 *  Dentro del crear se llaman los métodos build.
 *  Es válido siempre y cuando no se necesite alterar
 *  el orden del llamado a los "build's".
 */
abstract class OtroPizzaBuilder {
    protected Pizza pizza;

    public Pizza getPizza() { return pizza; }
    public void crearNuevaPizza() {
        pizza = new Pizza();
        buildMasa();
        buildSalsa();
        buildRelleno();
    }

    public abstract void buildMasa();
    public abstract void buildSalsa();
    public abstract void buildRelleno();
}

/** "Director" */
class OtraCocina {
   private OtroPizzaBuilder pizzaBuilder;
   public void construirPizza() {
        pizzaBuilder.crearNuevaPizza();
        //notar que no se necesita llamar a cada build.
    }
}
Prototype (Prototipo)

Este patrón nos permite duplicar objetos.

Para copiar un objeto necesitas crear uno nuevo y recorrer después todos los atributos del original y ponerlos en la copia. El problema es que no todos son accesibles. El truco aquí es delegar la clonación a los objetos, creando un método clone. Entonces, si un objeto permite la clonación se llama Prototipo.
abstract class Shape implements Cloneable {

    private String id;
    protected String type;

    abstract void draw();

    public String getType(){
        return type;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public Object clone() {
        Object clone = null;

        try {
            clone = super.clone();

        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }

        return clone;
    }
}
Singleton (Instancia única)

Para asegurarnos de que tener una sola instancia de un objeto, a la vez que proporciona acceso global a dicha instancia. Sólo es necesario un método y una variable estática.

Es muy utilizado para por ejemplo tener una sola instancia de base de datos compartido por varias partes del código.
class SingleObject {

    //create an object of SingleObject
    private static SingleObject instance = new SingleObject();

    //make the constructor private so that this class cannot be
    //instantiated
    private SingleObject(){}

    //Get the only object available
    public static SingleObject getInstance(){
        return instance;
    }

    public void showMessage(){
        System.out.println("Hello World!");
    }
}

Comentarios

Entradas populares de este blog

Django REST framework

Envío de checkboxes o selector multiple por AJAX con jQuery

Django: Modelos