El patrón de diseño Factory es uno de los patrones de diseño más comunes y útiles en Java. Se utiliza para crear objetos de una clase determinada sin necesidad de conocer el código específico de esa clase. En lugar de crear directamente una instancia de una clase, se utiliza un método factory para crear el objeto.
Un ejemplo típico de uso del patrón de diseño Factory es cuando tenemos una aplicación que necesita crear diferentes tipos de objetos en función de una entrada del usuario o de un archivo de configuración. En lugar de tener que escribir código específico para cada tipo de objeto, podemos utilizar una fábrica para crear los objetos necesarios.
Para implementar el patrón de diseño Factory en Java, necesitamos crear una interfaz que defina el método factory y luego crear una clase concreta que implemente esa interfaz. La clase concreta se encargará de crear los objetos necesarios.
Problema
Recapitulemos, eres programador, como programador te encantan las condiciones, desde que empezaste a programar es lo primero que aprendiste. Pero luego ves algo como esto:
public void createPayment(PaymentType paymentType) {
if (paymentType == PaymentType.CREDIT_CARD) {
createCreditCardPayment();
// More code here
} else if (paymentType == PaymentType.PAYPAL) {
createPaypalPayment();
// More code here
} else if (paymentType == PaymentType.DIRECT_DEBIT) {
createDirectDebitPayment();
// More code here
} else if (paymentType == PaymentType.DEBIT_CARD) {// More code here
createDebitCardPayment();
// More code here
} else if (paymentType == PaymentType.BITCOIN) {// More code here
createBitcoinPayment();
// More code here
} else if (paymentType == PaymentType.CASH) {// More code here
createCashPayment();
// More code here
} else if (paymentType == PaymentType.APPLE_PAY) {// More code here
createApplePayPayment();
// More code here
} else if (paymentType == PaymentType.GOOGLE_PAY) {// More code here
createGooglePayPayment();
// More code here
} else {
throw new NoSuchMethodException("Payment not found");
}
}
Wow, quiero decir, cuántos if y else hay, y luego cada método dentro de la clase lo hace más grande. Buena suerte para depurarlo y verificar en qué línea puede tener problemas, o si necesita hacer algunos cambios.
Una solución que se le puede ocurrir es usar la condición de cambio…
public void createPayment(PaymentType paymentType) {
switch (paymentType) {
case CREDIT_CARD:
// More code here
createCreditCardPayment();
// More code here
break;
case PAYPAL:
// More code here
createPaypalPayment();
// More code here
break;
case DIRECT_DEBIT:
// More code here
createDirectDebitPayment();
// More code here
break;
case DEBIT_CARD:
// More code here
createDebitCardPayment();
// More code here
break;
case BITCOIN:
// More code here
createBitcoinPayment();
// More code here
break;
case CASH:
// More code here
createCashPayment();
// More code here
break;
case APPLE_PAY:
// More code here
createApplePayPayment();
// More code here
break;
case GOOGLE_PAY:
// More code here
createGooglePayPayment();
// More code here
break;
default:
throw new NoSuchMethodException("Payment not found");
}
}
Bueno… para ser honesto, realmente no resuelve tanto, en realidad, necesitarías incluso unas pocas líneas de código más debido al “break”.
Solución: Patrón de diseño Factory
Necesitamos delegar la función a una clase diferente que se encargue de cada pago individual o también más funciones que se deben hacer.
Allí necesitamos crear una interfaz para que cada clase implemente nuestra funcionalidad requerida.
public interface Pago {
void crearPago();
}
Además de ello necesitaremos identificar que clase esta identificada con qué tipo de clase delegada. Es por eso que necesitamos crear la clase de fábrica.
public class PagoFactory {
private final static Map<TipoDePago, Pago> pagos = new HashMap<>(){{
put(TipoDePago.PAYPAL, new PagoPayPal());
put(TipoDePago.TARJETA_CREDITO, new PagoTarjetaCredito());
put(TipoDePago.TARJETA_DEBITO, new PagoTarjetaDebito());
put(TipoDePago.TRANSFERENCIA_BANCARIA, new PagoTransferenciaBancaria());
put(TipoDePago.BITCOIN, new PagoBitcoin());
put(TipoDePago.EFECTIVO, new PagoEfectivo());
put(TipoDePago.APPLE_PAY, new PagoApplePay());
put(TipoDePago.GOOGLE_PAY, new PagoGooglePay());
}};
public Pago obtenerPago(TipoDePago tipoDePago) {
return pagos.get(tipoDePago);
}
}
Y por último tenemos que juntarlo todo y podremos obtener el resultado dependiendo de nuestro tipo de pago:
public class Main {
public static void main(String[] args) {
PagoFactory pagoFactory = new PagoFactory();
Pago pago = pagoFactory.obtenerPago(TipoDePago.TARJETA_CREDITO);
pago.crearPago();
}
}