Design patterns: Factory pattern

D

Let’s look at one of the most common (and simplest too) design patterns: Factory method design pattern.

Requirement

Let’s start by looking at the requirement which can be solved by factory pattern. Let’s say you are building an Android app where you have two different versions available – Free and Paid. You are storing all the user data locally in the device in the free version and you are using database on your server to store data for the paid version. So, we start by creating an interface for our requirement with the required methods.

interface DataSource {
  fun get(): Data
  fun write(data: Data)
  fun delete(data: Data)
}

Now, we can create two implementations of this interface, one for the free version and another for the paid version.

class OfflineDataSource: DataSource {
  override fun get(): Data {
    //implement to get the data from the local device database
  }
  override fun write(data: Data) {
    //implement to write the data to the local device database
  }
  override fun delete(data: Data) {
    //implement to delete the data from the local device database
  }
}

And just like this, we have another class to perform the same operations but on an online data server.

class OnlineDataSource: DataSource {
  override fun get(): Data {
    //implement to get the data from the online database
  }
  override fun write(data: Data) {
    //implement to write the data to the online database
  }
  override fun delete(data: Data) {
    //implement to delete the data from the online database
  }
}

Now, when you want to use these classes in your code, you will have a check if the current user is a free user, then we use the offline class and otherwise, we will use the online class. The code to get the data will look something like this:

val dataSource = 
  if(currentUser.isPaidUser) {
    OnlineDataSource()
  } else {
    OfflineDataSource()
  }
dataSource.get()

This seems trivial. Let’s see the problems with this:

  • This code will be repeated everywhere we need to use data source interface
  • After we include this code everywhere, we will be in trouble when we need to change something. If at some later point, we decide to use the online database server for both type of users or we decide to add another data source, we will have to make this change at every place.

From the top itself, we can see that there is a lot of code duplication which we must avoid.

Solution

Well, if we have the same code at multiple places, we must use functions to store the repeated code and call the function from different parts of the code. And this is what exactly we are going to do here as well. Let’s create a class called DataSourceFactory and move the same logic there.

class DataSourceFactory {
  companion object {
    getDataSource(currentUser: User) {
      return if(currentUser.isPaidUser) {
          OnlineDataSource()
        } else {
          OfflineDataSource()
        }
    }
  } 
}

Now, we can simply use this code like this.

val dataSource = 
DataSourceFactory.getDataSource(currentUser)
dataSource.get()

Benefits we get with this:

  • Our code is much cleaner and we do not have any code duplication now.
  • When something changes, we just have to make the change to the factory class and we will get our changes everywhere in our application.
  • Our code doesn’t know about implementation details of the interface anymore. We are simply using the implementation provided by the factory method and we will be using whatever implementation is returned by the factory method. If we add, update or remove implementations for the same interface, our code doesn’t care about that and nothing needs to change except the factory class. This makes code more modular and easy to manage.

Well, this is it. This simple method to create a function to get the implementations of the interface is known as the Factory method pattern.

Conclusion

Factory method pattern is one of the most common design pattern and is being used in almost every project. If you randomly pick a project and check the source code, the chances are very high that you will find factory method pattern being used in the code. This simple pattern is very useful in making sure that we code to the interface and the implementation remains abstract.

Thank you for reading the article.

About the author

Akshay Jain

I am passionate about programming and feel amazing when I see people using the software I have contributed in. I believe it is essential to write high quality code, which is easy to understand and test. I am still a work in progress and decided to document and share some of my learnings with everyone. Apart from that, I like to read books, and so you might find a lot of book reviews on my blog. I am working as a Software Engineer at Oracle since post-graduation from IISc Bangalore, and happily married :)

Add Comment

Akshay Jain

I am passionate about programming and feel amazing when I see people using the software I have contributed in. I believe it is essential to write high quality code, which is easy to understand and test. I am still a work in progress and decided to document and share some of my learnings with everyone. Apart from that, I like to read books, and so you might find a lot of book reviews on my blog. I am working as a Software Engineer at Oracle since post-graduation from IISc Bangalore, and happily married :)

Get in touch