Design Patterns: Different approaches to use Factory pattern to choose objects dynamically at run time

Image for post
Image for post
Courtesy: Unsplash

Factory design pattern is probably one of the most used design patterns. If you have ever cared about doing low level object model properly for your service or if you have ever studied design patterns, it’s highly probable that you have incurred some scenario where you can use Factory pattern. But this post is not just about basic knowledge of Factory pattern, rather it discusses about how you can choose objects dynamically at the run time with different approaches & their pros and cons.

What is Factory Pattern & When to use it?

Factory is a creational design pattern which is used to create different implementation objects of the same type. Let’s consider a simple example below:

You have to design a logger API (not a web service API but a class level method ) which can log to different mediums like:

  • In memory data structure.
  • File on disk.
  • Database ( local or remote ).
  • Remote storage service like Amazon S3 or Google Cloud Storage.

Say you have planned to implement the following API:

where loggerMedium can be MEMORY, FILE, DB, REMOTE_SERVICE .

Till now it’s all fine. But how do you implement the logic in the back end?

In a naive way you can implement like following:

What happens when you add a different storage medium like FLASH_DRIVE? You will write another method & another if else in the class? With more numbers of mediums, the class is going to explode.

Is there any better way to design the above logic?

Another way is to have separate classes for different storage mediums & patch them into the Logger class. Like below:

In this implementation, we have separated out the individual code into their corresponding files, but our Logger class is tightly coupled with the instances of the storage mediums like FileLog, DBLog etc. With more additions of storage medium, more instance will be introduced in the Logger class.

Any other better way?

Since all storage mediums are just doing the same work in a different way, we can define a common contract which all the storage implementations follow & the Logger class just knows that it has to invoke that contract to get that work done. Let’s define that contract using interface:

Now all storage medium classes will implement this interface to conform to the contract:

In the Logger class, now you don’t need to have tightly coupled instances of there classes. Since all of them implement the same contract, you just need to be able to choose the most matching instance depending on the parameter passed to the Logger. This is where Factory pattern comes into picture — depending on the parameter passed, choose an implementation dynamically at the run time. Let’s define a factory class which can help you do so:

LoggerFactory helps to choose the proper storage instance by examining the parameter — loggerMedium passed to the getInstance method. Our Logger class just have to use it & pass the parameter:

The code has become very uniform here, responsibility of creating actual storage instances have been shifted to LoggerFactory, individual storage classes just implement how they are going to log the message to their particular medium & finally the Logger class just concerns about getting the appropriate storage instance through the LoggerFactory & delegates the actual logging to the implementation. In this way, the code is very loosely coupled. You want to add a new storage medium like FLASH_DRIVE, just create a new class which implements LoggingOperation interface & register it to the LoggerFactory with proper parameter. This is how factory pattern can help you dynamically choosing an implementation.

Can we do better?

We have already done a loosely coupled design but imagine a scenario where we have hundreds of storage mediums, so we would end up with hundreds of instances of storage mediums & hundreds of if else inside the LoggerFactory class. This looks bad & it has the potential to become a tech debt if not managed properly. We can try to definitely do better by reducing the if else . How can we do that?

One way to get rid of ever growing if else can be maintaining a list of all implementation classes in the LoggerFactory class like below:

Note that in all of the above implementations, the LoggerFactory is tightly coupled with instances of the storage mediums either through if else or list as above. More storage instances, more modifications in the LoggerFactory, also registration of the storage instances is the responsibility of the factory. What if we do the reverse & individual instances registers themselves to the factory. In this way, the factory does not need to know about what instances are available in the system rather the instances themselves register & the factory just serves them if they are available in the system. Let’s make the following change to the LoggerFactory:

Here, LoggerFactory contains a map of loggerMedium to LoggingOperation instance, the key of the map can be computed in any way as you want — it’s up to you. Storage instances use the LoggerFactory.register() method to register themselves. LoggerFactory.getInstance() just checks if the logger exists in the system, if yes returns the instance.

Now all storage classes should register themselves to the LoggerFactory like below:

Since the registration should happen only once, it happens in the static block when the storage classes are loaded by the class loader.

By default JVM does not load classes unless it’s instantiated or invoked externally by the user application. So although the storage classes register themselves, the registration in reality won’t happen unless they are loaded by the Java class loader. You have to force the loading when the LoggerFactory is invoked since LoggerFactory is the entry point while getting the appropriate storage instance. How do you do that?

Modify the LoggerFactory like the following:

In the above implementation, we are using a method called loadCLasses which scans the provided package name ( package names should be provided in this format: com.example.factory.impl) & loads all the classes residing at that directory to the class loader. In this way when the classes loads, their static block gets initialized & they register themselves to the LoggerFactory. From this point on wards, the map inside the LoggerFactory knows about all the instances & it can return appropriate instance accordingly. The above code snippet uses IOUtils from Apache IO project, you can add that dependency through maven or gradle.

How to implement this technique in Spring or SpringBoot?

This was an example with POJO objects. If you use Spring or Springboot, you can mark the storage classes with @Component annotation, so spring automatically loads the classes when the application starts, they register themselves, you don’t need to use loadClasses functionality in that case, Spring takes care of loading classes. Also in this case, inside the static block of storage classes, instead of doing LoggerFactory.register("REMOTE", new RemoteServiceLog()), since it’s now a spring bean, just register the class types as following & in the LoggerFactory.getInstance(), retrieve the beans by their class types:

Change LoggerFactory:

Change all storage implementation classes like below:

When you call LoggerFactory, you have to pass the Spring ApplicationContext bean to it since you can’t get in the static method.

Advantage of this factory implementation:

  • It’s very easy to manage. While adding a new storage class, just put that class in the particular package where other similar classes are, register itself in the static block & you are done.
  • Less code changes & no more if else in the factory classes, in fact you don’t need to make any change in the factory class when you add a new storage implementation.
  • It also follows all SOLID principle of programming.

Disadvantages:

  • If you are using POJO implementation, java reflection is expensive since it involves I/O operations. But if you are using Spring or SpringBoot, there is no such worry since the framework itself invokes the components.
  • The developer has to remember that s/he has to write a static block where the implementation class registers itself. It’s a small price to pay though.

Conclusion:

We have seen different ways of implementing factory, each of them has their own pros & cons. But in my opinion, the last approach is better since it’s clean, prone to less code changes & adhered to Object Oriented Design principles.

Written by

Deep discussions on problem solving, distributed systems, computing concepts, real life systems designing. Developer @PayPal. https://in.linkedin.com/in/kousikn

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store