Uno de los patrones de diseño más utilizados en Java es el patron Factory que es un patrón de diseño creacional y que sirve para construir una jerarquía de clases. Sin embargo a veces a la gente le cuesta ver como usar este patrón en su código. Vamos a utilizar un ejemplo sencillo en el que tendremos una jerarquía de clases factura como se muestra a continuación.
El código será el siguiente:
package com.arquitecturajava; public abstract class Factura { private int id; private double importe; public int getId() { return id; } public void setId(int id) { this.id = id; } public double getImporte() { return importe; } public void setImporte(double importe) { this.importe = importe; } public abstract double getImporteIva(); }
package com.arquitecturajava; public class FacturaIva extends Factura{ @Override public double getImporteIva() { // TODO Auto-generated method stub return getImporte()*1.21; } }
package com.arquitecturajava; public class FacturaIvaReducido extends Factura{ @Override public double getImporteIva() { // TODO Auto-generated method stub return getImporte()*1.07; } }
Como vemos la clase Factura es una clase abstracta de la cual heredan nuestras dos clases concretas que implementan el cálculo del IVA. Vamos a construir una Factoría para que se encargue de construir ambos objetos de la jerarquía.
package com.arquitecturajava; public class FactoriaFacturas { public static Factura getFactura(String tipo) { if (tipo.equals("iva")) { return new FacturaIva(); } else { return new FacturaIvaReducido(); } } }
Si nos fijamos la clase lo único que hace es instanciar un objeto u otro dependiendo del tipo que le solicitemos. Eso en un principio parece poco práctico. Pero vamos a ver como queda el programa main:
package com.arquitecturajava; public class Principal { public static void main(String[] args) { Factura f= FactoriaFacturas.getFactura("iva"); f.setId(1); f.setImporte(100); System.out.println(f.getImporteIva()); } }
Nos podemos dar cuenta que el programador ya solo tiene que tratar con el concepto de Factura para el la clase FacturaIva y FacturaReducido no existen.
Esto permite una simplificación a la hora de trabajar clara. Es cierto que las Factorias se encargan de generar una jerarquía de clases pero su función fundamental es encapsular una jerarquía de objetos y reducir el conjunto de conceptos con los que trabajamos.
Otros artículos relacionados: Singleton , Delegación ,Adaptadores
Los botones de compartir entorpecen enormemente la lectura del artículo
Anda no me había dado cuenta , que estas desde una tablet o desde un móvil?. Gracias por el aporte
Muchas gracias por el aporte, últimamente estoy intentando comprender los patrones y el diseño OO en general. He visto aplicar este patrón de varias maneras diferentes pero esta, ¿no infringe el principio Open-Closed?.
Si queremos un IVA diferente, está bien tener que crear una nueva clase para ello, pero también habría que modificar FactoriaFactura.
if(tipo.equals(“iva”))
return new FacturaIva();
else if(tipo.equals(“ivaX”))
return new FacturaIvaX();
else
return new FacturaIvaReducido();
Corrígeme si me equivoco pero, ¿no sería mejor crear una interface y tener una sola clase Factura con un atributo de ese tipo?, y así crear distintas clases IVA que la implementen como deseemos.
interface TipoIva {
double valorIva();
}
public class Factura {
private int id;
private double importe;
private TipoIva iva;
public Factura(TipoIva iva){
this.iva=iva;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public double getImporte() {
return importe;
}
public void setImporte(double importe) {
this.importe = importe;
}
public double getImporteIva(){
return importe * iva.valorIva();
}
}
De esa manera la factoria quedaría así para siempre:
public class FactoriaFacturas {
public static Factura getFactura(TipoIva iva) {
return Factura(iva);
}
}
Si queremos iva reducido:
Factura f= FactoriaFacturas.getFactura(new IvaReducido());
o iva normal:
Factura f= FactoriaFacturas.getFactura(new Iva());
O cualquier tipo de iva que se nos ocurra en el futuro añadir.
Si quisieras ser estricto podría usar el api de reflection para instanciar los distintos tipos de objetos de la factoria 🙂
Es otra posibilidad (aunque personalmente no me gusta el uso de reflection a no ser que sea estrictamente necesario), es lo bonito de la programación, nadie tiene la respuesta única y definitiva!.
Aunque es cierto que a veces ser estricto de más también trae problemas y puede llevar a construir grandes arquitecturas “por si acaso” que no se van a utilizar jamás jajaj, aún así consideré que era bueno compartir mi punto de vista para los que se pasen por aquí, ahora en lugar de llevarse una solución, se llevan 3 😉
Saludos!
Estoy con José, Yo creo que si además hay alternativa al reflection mejor que mejor, que se suele ser bastante lento y como te despistes te pueden llegar a hacer ejecutar un código que no te interesa. A mí se me ocurrió lo siguiente, si veis inconvenientes me comentáis.
public interface IVA {
public Double getIVA();
}
********************************
public abstract class Factura implements IVA{
private Double importe;
public enum IVA{ GENERAL(Double.valueOf(21)), REDUCIDO(Double.valueOf(10)), SUPERREDUCIDO(Double.valueOf(4));
private final Double value;
IVA(Double value){
this.value = value;
}
public Double getValue(){
return this.value;
}
};
public Double getImporte() {
return this.importe;
}
public void setImporte(Double importe) {
this.importe = importe;
}
public Double getImporteConIva() {
return this.getImporte() + getIVA()*(this.getImporte())/100;
}
}
*******************************************
public class FacturaReducida extends Factura implements IVA{
@Override
public Double getIVA() {
return IVA.REDUCIDO.getValue();
}
}
**********************************************************
public class FacturaGeneral extends Factura implements IVA{
@Override
public Double getIVA() {
return IVA.GENERAL.getValue();
}
}
********************************************************
public class AppFacturaMain {
public static void main(String[] args) {
Factura factura = new FacturaReducida();
factura.setImporte(Double.valueOf(50));
IVA iva = new FacturaReducida();
System.out.println(“total:” + factura.getImporteConIva());
System.out.println(“iva:” + iva.getIVA());
}
}
Hola
Hola Cecilio, gracias por la explicación del patrón factory
al parecer te falto que el método “getFactura” de la clase “FactoriaFactura” sea de tipo estático, para que la aplicación funcione
saludos
muchas gracias por la correción ricardo.