Spring Boot - Implementing Factory Pattern

Ömer Kurular
3 min readMay 9, 2021

--

Factory pattern is a creational pattern where all the object creation logic resides in a factory class. In this article, I am going to talk about how to use it in a Spring Boot application. So, let’s start

Imagine we have Viewer class which does as its name suggests viewing operation of different types of media such as document, video, image etc. We have the following model classes which are empty as its logic is not a interest for us.

public class Document {}public class Image {}public class Video {}

So, in our application, we can of course directly create objects of the above class or we can assign the creational logic to viewer factory. We will pick the second way as they implement a common interface and client does not need to know the creational logic. We also need to check viewer classes of the above models before continuing. Firstly, you can see the Viewer interface which provides common methods of all viewers, getType and view. getType will be used for factory implementation and view will be the main logic for viewing the object.

public interface Viewer<T> {
ViewerType getType();
void view(T object);
}

Below, you can see the Viewer implementing classes.

DocumentViewer

@Component
public class DocumentViewer implements Viewer<Document> {
private static final ViewerType VIEWER_TYPE = ViewerType.DOCUMENT;
@Override
public ViewerType getType() {
return VIEWER_TYPE;
}

@Override
public void view(Document object) {
// some logic to view image,
// we do not care the implementation here
}
}

ImageViewer

@Component
public class ImageViewer implements Viewer<Image> {
private static final ViewerType VIEWER_TYPE = ViewerType.IMAGE;

@Override
public ViewerType getType() {
return VIEWER_TYPE;
}

@Override
public void view(Image object) {
// some logic to view image,
// we do not care the implementation here
}
}

VideoViewer

@Component
public class VideoViewer implements Viewer<Video> {
private static final ViewerType VIEWER_TYPE = ViewerType.VIDEO;
@Override
public ViewerType getType() {
return ViewerType;
}

@Override
public void view(Video object) {
// some logic to view image,
// we do not care the implementation here
}
}

and ViewerType enum

public enum ViewerType {
DOCUMENT,
IMAGE,
VIDEO
}

Now, let’s get to the main part, the factory class. Firstly, you can see the viewerMap. In this map, we will store viewers so as not to create them each time getViewer method is called to create the desired object. Then, in our constructor, we will get the list of classes (Spring provides it for us) implementing Viewer interface and then we put them in our map. The main client-concerning part in this class is getViewer method. When this method is called with desired type, it will first fetch the Viewer from the map. If it is a null object, then it means we are sending errorous viewer type meaning that kind of viewer is never implemented. If no such situation occurs, then we can continute and return the viewer.

@Component
public class ViewerFactory {
private static final Map<ViewerType, Viewer> viewerMap;

@Autowired
private ViewerFactory(List<Viewer> viewers) {
viewerMap = viewers.stream().collect(Collectors.toUnmodifiableMap(Viewer::getType, Function.identity()));
}

public static <T> Viewer<T> getViewer(ViewerType viewerType) {
return Optional.ofNullable(viewerMap.get(viewerType)).orElseThrow(IllegalArgumentException::new);
}
}

We can use it as follows

ViewerFactory.getViewer(ViewerType.DOCUMENT).view(new Document());

I also want to share another way to implement Factory class as follows. In this case, I omitted static field and method and used factory by autowiring it in the needing class.

@Component
public class ViewerFactory {
private final Map<ViewerType, Viewer> viewerMap;

@Autowired
private ViewerFactory(List<Viewer> viewers) {
viewerMap = viewers.stream().collect(Collectors.toUnmodifiableMap(Viewer::getType, Function.identity()));
}

public Viewer getViewer(ViewerType viewerType) {
return Optional.ofNullable(viewerMap.get(viewerType)).orElseThrow(IllegalArgumentException::new);
}
}

A service to use it

@Service
public class ViewerService {
private final ViewerFactory viewerFactory;

public ViewerService(ViewerFactory viewerFactory) {
this.viewerFactory = viewerFactory;
}

public void view(ViewerType viewerType, Object o) {
viewerFactory.getViewer(viewerType).view(o);
}

@PostConstruct
public void test() {
view(ViewerType.VIDEO, new Video());
view(ViewerType.DOCUMENT, new Document());
view(ViewerType.IMAGE, new Image());
}
}

As you see, it is very easy to implement factory pattern in Spring Boot applications. You can find codes in the repository below. I hope you enjoy the article and find it useful. See you next time :)

https://github.com/kurular4/medium-spring-factory

--

--

Ömer Kurular
Ömer Kurular

Written by Ömer Kurular

I am Ömer and currently working as a full-time software engineer. I will share my knowledge with you I gained through years of professional and self working.

Responses (4)