El patrón de diseño Factory es usado principalmente cuando tenemos una clase o interfaz con muchas subclases o implementaciones y según algún input necesitamos devolver una de estas subclases concretas.
Cuando crear una Factory class
Pensemos en una interfaz de la cual realizamos luego varias implementaciones. Usemos la típica interfaz de ejemplo Shape de la cual implementamos varias clases Circle, Rectangle, Square.
package patterns.factory;
public interface Shape {
void draw();
}
package patterns.factory;
public class Circle implements Shape {
@Override
public void draw() {
System.out.println("I am a Circle");
}
}
package patterns.factory;
public class Rectangle implements Shape {
@Override
public void draw() {
System.out.println("I am a Rectangle");
}
}
package patterns.factory;
public class Square implements Shape {
@Override
public void draw() {
System.out.println("I am a Square");
}
}
En este caso tenemos varias implementaciones y según el tipo de Shape que deseamos queremos crear la implementación concreta de la interfaz.
La forma habitual (sin un factory) sería de este modo.
package patterns.factory;
public class FactoryPatternExample {
public static void main(String[] args) {
FactoryPatternExample example = new FactoryPatternExample();
example.printShape("CIRCLE");
}
// Whithout factory
public void printShape(String shapeType) {
Shape shape;
switch (shapeType) {
case "CIRCLE":
shape = new Circle();
break;
case "RECTANGLE":
shape = new Rectangle();
break;
case "SQUARE":
shape = new Square();
break;
default:
throw new IllegalStateException("Unexpected value: " + shapeType);
}
shape.draw();
}
}
Como crear una Factory class
Vemos en el ejemplo previo que podríamos simplificar la creación de la clase llevándonos todo lo que esta en el switch a otra clase Factory que se encargue de esto, dejando así nuestro código más simple y claro.
Vamos a crear una clase ShapeFactory para tal fin.
package patterns.factory;
public class ShapeFactory {
public static Shape getShape(String shapeType) {
if (shapeType == null) {
return null;
}
if (shapeType.equalsIgnoreCase("CIRCLE")) {
return new Circle();
} else if (shapeType.equalsIgnoreCase("RECTANGLE")) {
return new Rectangle();
} else if (shapeType.equalsIgnoreCase("SQUARE")) {
return new Square();
} else {
throw new IllegalStateException("Unexpected value: " + shapeType);
}
}
}
Veamos cómo queda ahora nuestro ejemplo previo haciendo uso de nuestra Factory.
Observa que queda mucho más limpio y claro delegando la creación de las instancias a la Factory.
La Factory será ahora la encargada de crear la instancia en particular y nuestra clase main de ejemplo hará uso de esta factory olvidandose de la complejidad para crearla.
package patterns.factory;
public class FactoryPatternExample {
public static void main(String[] args) {
FactoryPatternExample example = new FactoryPatternExample();
example.printShape("CIRCLE");
}
// Using Factory
public void printShape(String shapeType) {
Shape shape2 = ShapeFactory.getShape(shapeType);
shape2.draw();
}
}
Obviamente este es un ejemplo muy sencillo, en general las Factory class pueden ser más complejas encargándose de crear las instancias bajo condiciones de negocio o diversos inputs que hacen que su uso sea conveniente.
Puedes descargar este código en github