Factory design pattern

Dominik Szczepaniak
5 min readJul 10, 2022

This article was originally written in Polish and published on my blog.

The Factory is a pretty popular and simple to use design pattern. The literature distinguishes 4 types of factories:

  • Factory
  • Factory Method
  • Static Factory
  • Abstract Factory

The Factory belongs to the creational design patterns family.

In this article, I’m going to cover and illustrate all the mentioned Factory types. The main idea of the Factory, as in the case of a real factory, is to create objects. Using Factories allows for hiding implementation details of creating objects and separating them from the business logic layer. In the article, all variations of the Factory design pattern will be explained via short code snippets in TypeScript. Fortunately, to understand the prepared snippets being familiar with only the basics of object-oriented concepts is required. Okay, so let’s get crackin’!

Factory

The best way to show the practical use case and benefits of using the Factory design pattern is by showing it in the example. The system you maintain includes a sales system and needs to generate invoices for customers. As your customers are from various countries, the invoices have to be suited for their needs. The examples of adjustments are:

  • the date format e.g. dd/mm/yyyy, mm/dd/yyyy;
  • the text direction — left-to-right or right-to-left;
  • the currency.

Potentially it creates a large number of combinations and possibilities and handling it inside the business logic would cause the business code unreadable and hard to maintain. It would also make the business code error-prone and harder to extend. The step forward in the design of the solution is to create a simple Factory where all types of invoices could be wrapped into a separate private method. The created factory would expose only one public method with a param object as an expected input. Based on parameter values a ready-to-print invoice will be produced. The business logic code knows nothing about the details of creating invoices for specific countries. The Factory is treated as a black box that creates an invoice based on the provided parameters. The most trivial implementation of the Factory may look as follows (I intentionally skipped parts of code that are not required to illustrate the Factory pattern):

When the Factory implementation is ready, all you have to do is to create an InvoiceFactory instance and call the createInvoice method. The result of calling the mentioned method is a ready-to-print invoice that can be returned to the customer by the piece of code responsible for handling the business logic. The invoice generation process in the business logic code may look as follows:

The current implementation allows for creating invoices for customers from Poland, the United States, and Saudi Arabia. To add support for other countries the created Factory can be very easily extended. All you need to do is to create a new case in the public method, a new private method, and a new specific class for a particular country. Note that I decided to create a separate class for each country. Otherwise, the Factory would end as one bloated class that for sure you’d like to avoid.

Factory Method

The solution proposed in the previous section has one significant issue. Excluding parts of the code that are specific to a particular country, there’s a part of the code that is common for each invoice such as file format or font settings (size, color, family, etc.).

To resolve this issue the Factory Method design pattern can be used. Thanks to this, it’s possible to extract a part of code that is common for all the invoice classes. The previous implementation converted to a Factory Method implementation may look as follows:

The common part of the process of generating invoices has been wrapped in the _generateInvoice method in the abstract Invoice class. The specific implementations for particular countries are implemented in particular classes in the protected format method. The above code combines the Factory Method and Factory design patterns. It shows that design patterns can be used together and mixed to get the desired results! Combined patterns caused the solution to be clear, easy to extend, and free from code duplication.

The simplicity of the solution causes that introducing a new software engineer to this piece of the code will take less time and boost their productivity. The engineer does not have to be familiar with the low-level logic related to the process of generating invoices. All they have to do is to implement a very simple class with a finite number of options and attach it to the factory.

source: pixabay.com

Static Factory

When the factory is ready another issue appeared. In the system, there were a few instances of the InvoiceFactory created. A few customers received an invoice with the same id. To eliminate this issue we could introduce a Static Factory. In this case, we can implement a simple in-memory solution that will store the ids of already generated invoices to prevent the described situation. In the case of duplication, an error will be thrown. The example implementation of the Static Factory may look as follows:

Static Factories are also helpful in the case of various helpers and utils classes. In this case, pretty often it does not make sense to instantiate a factory class, and relying on static methods is simpler. Calling HelperClass.doX( params )is way simpler than creating a new instance of a HelperClass.

Abstract Factory

The described sales system has been successfully shipped into production. After some time a new set of requirements have been delivered to your team. Now, not only invoices but also internal sales reports have to be generated by the system. All types of documents have to be able to generate via a single interface. Moreover, the system must be ready to be extended by new document types in the future. The solution to this issue is to create an Abstract Factory. In short, the Abstract Factory uses other factories in its implementation and is a Factory at the same time. The example of the Abstract Factory is shown by the code below:

The prepared solution meets all the mentioned business requirements. Here I have to underline that all the factories must return a document that meets the IDocument interface. Abstract Factories are the perfect solution if your system produces various objects with a common interface. In our case all the created objects are documents.

Summary

I hope, after reading the article the idea of the Factory design pattern is clear to you. I encourage you to try the described factories out in your projects. I also will be grateful if you give a clap and leave a comment! Below you’ll find some extra materials and sources.

Sources and extra materials

--

--

Dominik Szczepaniak

Professionally Software Engineer at CKSource. Privately a blogger, a fan of Italian cuisine, and a fan of cycling and weight training.