Factory pattern is a creational pattern where the logic of creating an object of the class resides in a factory class, in this article, we will see how to implement a factory pattern that always returns a new instance of the class ( prototype scope) in Springboot
Will take a simple and common example for the factory design pattern, draw the shape. We have an interface shape and multiple implementations of the shape let say Square and Rectangle classes and a factory method which will get the name of the shape as an argument and return the object of the shape using if / switch. this is the usual way of implementing the factory pattern in Core Java. In Springboot with the dependency injection ( IoC ) the objects are created and injected automatically. we will see how to create a new object every time and implement the factory pattern.
First we create an interface shape
public interface Shape {
String draw();
}
Enum for Shapes
Then we will create an ENUM of shapes which we will be used for annotating the Shape implementation
public enum ShapesType {
SQUARE,
RECTANGLE
}
Annotation for the Shapes
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Shapes {
ShapesType[] command();
}
We created this annotation class to use it in all the implementation of the Shapes Interface, this annotation will help the factory to identify which class to use for each shape type. Here in ShapeTypes I use array, this will be helpful in some use cases like Square and Rectangle we can use the same implementation class
Then we will have two implementation Square and Rectangle
Square Implementation
@Shapes(command = SQUARE)
@Service
@Scope(value = "prototype")
public class SquareImplementation implements Shape {
@Override
public String draw() {
return Integer.toHexString(System.identityHashCode(this));
}
}
Rectangle Implementation
@Service
@Shapes(command = RECTANGLE)
@Scope(value = "prototype")
public class RectangleImplementation implements Shape {
@Override
public String draw() {
return Integer.toHexString(System.identityHashCode(this));
}
}
Shape Factory
In this factory, the handler is @Autowired , while loading the application all the implementation of the shape interface is populated in the handlers variable
@Autowired
private ShapeFactory(List<Shape> handlers) {
this.handlers = handlers;
}
And in @PostConstruct , we fetch the annotation of all the handlers and use that shape as a key and the handler object as the value.
@PostConstruct
public void initMyServiceCache() {
handlers.forEach(service -> {
Shapes annotation = service.getClass().getAnnotation(Shapes.class);
ShapesType[] commands = annotation.command();
for (ShapesType command : commands) {
myServiceCache.put(command, service);
}
});
}
A method getShapeImplementation which uses the map and get the value from the Key of shapes
public static Shape getShapeImplementation(ShapesType shapesType) {
Shape service = myServiceCache.get(shapesType);
if (null == service) {
throw new RuntimeException("Unknown shape type: " + shapesType);
}
return FactoryUtil.getBean(service.getClass());
}
In this, there is a class called FactoryUtil and has a method getBean which is used to get the new instance of the Shape class whenever it gets invokes using the Springboot Application Context-Aware implementation, read more about the scoped bean injection problem here.
@Service
public class FactoryUtil implements ApplicationContextAware {
private static ApplicationContext context;
public FactoryUtil() {
}
public static <T> T getBean(Class<T> beanClass) {
return context.getBean(beanClass);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
context = applicationContext;
}
}
Take away
In this article, we have seen the ways to use the Spring bean dependency injection and the prototype scope to create a new object of the implementation class for the factory design pattern, if you have hundreds of shapes also the code remains the same and you need to add more ENUM and shape implementation, no need to keep adding an if statements or the cases to the switch.
The full example of this code you can be found in my GitHub repository
https://github.com/asvignesh/springbootfactory
Also published on Medium.