Tiempo de lectura: 4 minutos

Patrón Factory Method – Método de Factorías

El patrón Factory o Factorías es uno de los patrones más usados aunque también se tiende a decir que es el patrón que peor se usa.

Factory Method es un patrón de creación de objetos y normalmente lo suelo usar para solucionar el problema que se presenta cuando tenemos que crear la instancia de un objeto pero a priori no sabemos aún que tipo de objeto tiene que ser, generalmente, porque depende de alguna opción que seleccione el usuario en la aplicación o porque depende de una configuración que se hace en tiempo de despliegue de la aplicación.

Este patrón está compuesto por:

  • Product: Define la interfaz de los objetos que de van a crear.
  • ConcreteProduct: Implementación de la interfaz.
  • Creator: Declara la factoría y devuelve un objeto del tipo producto. También puede definir una implementación por defecto para la factoria que devolvería un objeto ConcreteProduct por defecto.
  • ConcreteCreator: Sobreescribe el método de la factoría para devolver una instancia concreta de un objeto ConcreteProduct.

Para entenderlo mejor, veamos un ejemplo.

Problema

Tenemos una tienda online donde se le ofrece al usuario tres métodos de pagos distintos: tarjeta, paypal o transferencia bancaria.

 Product

/**
 * Ejemplo patrón Factory Method.
 *
 * Product
 */
public interface Payment {

    public boolean isValidLogin(Login login);

    public void doPayment(ShoppingList shoppingList);

}

ConcreteProduct

Pago con Tarjeta – Card:

/**
 * Ejemplo patrón Factory Method.
 *
 * ConcreteProduct
 */
public class CardPayment implements Payment {

    @Override
    public boolean isValidLogin(Login login) {
        //todo implementar la lógica del login para la tarjeta.
        return true; //valor para el ejemplo.
    }

    @Override
    public void doPayment(ShoppingList shoppingList) {
        //todo se implementa el método/para llevar acabo el pago con el TPV elegido.
        //...
    }
}

Pago con Paypal

/**
 * Ejemplo patrón Factory Method.
 *
 * ConcreteProduct
 */
public class PaypalPayment implements Payment {

    @Override
    public boolean isValidLogin(Login login) {
        //todo implementar la lógica del login para PayPal
        return true; //valor para el ejemplo.
    }

    @Override
    public void doPayment(ShoppingList shoppingList) {
        //todo hacer la implementación de la API de Paypal
    }
}

Pago por Transferencia Bancaria:

/**
 * Ejemplo patrón Factory Method.
 *
 * ConcreteProduct
 */
public class WireTransferPayment implements Payment {

    @Override
    public boolean isValidLogin(Login login) {
        //todo implementar la lógica del login para la transferencia bancaria.
        return true; //valor para el ejemplo.
    }

    @Override
    public void doPayment(ShoppingList shoppingList) {
        //todo hacer la implementación de la API de la transferencia
    }
}

Concrete Creator & Creator

/**
 * Ejemplo patrón Factory Method.
 *
 * Creator - ConcreteCreator
 */
public class PaymentFactory {

    public enum TypePayment { CARD, PAYPAL, WIRE_TRANSFER }

    public static Payment getPayment(TypePayment typePayment){
        switch (typePayment){
            case CARD:
                return new CardPayment();
            case PAYPAL:
                return new PaypalPayment();
            case WIRE_TRANSFER:
                return new WireTransferPayment();
            default:
                //Para hacerlo más entendible por defecto devolvemos tarjeta.
                return new CardPayment();
        }
    }

}

MainPayment – Clase para probar el patrón.

/**
 * Clase principal para realizar el pago.
 */
public class MainPayment {

    private Payment payment;

    /**
     * Se obtiene la forma de pago elegida por el usuario. En esta clase no importa que pago haya
     * elegido, para nosotros es una abstracción.
     *
     * @param typePayment
     */
    public MainPayment(PaymentFactory.TypePayment typePayment){
        this.payment = PaymentFactory.getPayment(typePayment);
    }


    public boolean isLogged(Login login){
        return (payment.isValidLogin(login));
    }

    /**
     * Método para realizar el pago de una lista de compra.
     *
     * @param shoppingList Lista de Compra
     *
     * Como podemos ver, podemos ampliar los tipos de pagos sin que el proceso se vea alterado.
     */
    public void payment(ShoppingList shoppingList){
        payment.doPayment(shoppingList);
    }

}

Como habéis podido observar, si el día de mañana tenemos que añadir una nueva forma de pago, sólo habría que crear un nuevo Concrete Product y añadirlo como opción al Concrete Creator/Creator sin que el proceso de compra (MainPayment) se viera alterado.

Espero que este ejemplo sencillo sea lo suficientemente didáctico. Añadiré más ejemplos en el repositorio Git.

Fuentes:

  • Repositorio GitHub con el ejemplo. Ver

Bibliografía: