In this article of our design pattern series, we will continue our learning of design patterns and cover the Factory Design Pattern in Java. We will take a look at the different uses of factory design pattern and how to implement it in Java.
1. Factory Design Pattern
The Factory design pattern is one of the creational patterns and we can find its uses in almost all the libraries in JDK, Spring. We will understand what this pattern is about using a Java application. We will then see the design benefits of this pattern.This factory design pattern provides an interface for creating objects in a super-class, but allows sub-classes to alter the type of objects that will be created.
The Factory design pattern or Factory Method design pattern is best suited in the situation where a super-class (Interface, Abstract Class, or non-final Classes) has multiple sub-classes (implementations) and at the run time we choose the exact implementing class based upon the input. The Factory will be responsible for creating and instantiating the implementing class and giving it to the client program. Factory pattern uses reflections internally to instantiate the classes and doesn’t use new operator directly.
1.1. Problem Statement
To understand factory design pattern more easily, let’s take an example where we are building a banking application. The initial release is focused on providing a saving account for the customers so most of our code and focus will be on the SavingAccount
class. But we have the plan to launch other type of accounts for the customers.
- Current Account
- Business Account
- Personal Account
How will you handle this situation where you are focused on a specific case but you have plan to release new variations in the future?
1.2. Solution
The factory design pattern comes as a rescue in such cases where you may have multiple implementing for a given type and at the run time we choose the exact implementing class based upon the input. The factory design suggest to replace the direct object creation with the special factory method. The factory method will still be using the new
keyword but this will handled entirely within the factory class. Hang on if you are still not sure as it seems moving object creation from one class to another
Important to note that the Factory design pattern is one of the most used Java design patterns for creating objects. The client will get newly instantiated objects using a common interface.
2. Factory Design Pattern in Java
To understand this design pattern, let’s use an example of an banking application. in our banking application, we want to offer the following type of account to the customer
- Personal
- Checking
- Business
We also want to keep the possibility of offering other account type in future. Also note that each account type will have it’s own rule and processes and we can’t have a single class handling all type of accounts. This is how the UML
or flow diagram looks like using our factory design pattern in
Let’s see it’s implementation in Java
2.1. Superclass
As mentioned above the superclass in the Factory design pattern can be interfaces, abstract classes, or non-final classes. Let’s take an example of Bank Accounts like Personal, Business, and Checking and use the BankAccountFactory
to create these accounts as per the client’s requirement. We will first see the superclass which in our case is an interface BankAccount
, this interface will have a single method registerAccount()
.
public interface BankAccount {
public void registerAccount();
}
2.2 Subclasses
For our example we have 3 implementing classes that will implement the BankAccount
interface, below are code for the same, do notice they will have their way of implementing the registerAccount()
method.
Personal Account
public class PersonalAccount implements BankAccount {
@Override
public void registerAccount() {
System.out.println("Creating a personal account");
}
}
Business Account
public class BusinessAccount implements BankAccount {
@Override
public void registerAccount() {
System.out.println("Creating a business account");
}
}
Checking Account
public class CheckingAccount implements BankAccount {
@Override
public void registerAccount() {
System.out.println("Creating a checking account");
}
}
Don’t pay much attention to the logic inside these classes as that will be based on your requirement. Keep in mind that each class in overriding the registerAccount()
method which is the key for our use case.
2.3. Factory Class
The factory class is the main component of the factory design pattern. This class return the object of required class based on the input parameter. If you observe, the main purpose of our factory class is to return the object at run time.
public class BankAccountFactory {
public BankAccount createAccount(String type) {
BankAccount bankAccount = null;
if (type.equals("P")) {
bankAccount = new PersonalAccount();
} else if (type.equals("B")) {
bankAccount = new BusinessAccount();
} else if (type.equals("C")) {
bankAccount = new CheckingAccount();
} else {
System.out.println("Invalid type");
}
return bankAccount;
}
}
The registerAccount()
method in the above code is a factory, it is taking input from the caller, and based on that input value it is creating the corresponding implementation class, in case the given input is invalid, we will return a “invalid type
” message or we can also throw an exception.
Let’s see how to use the factory class of our design to get the object at run time.
public class Branch {
public static void main(String[] args) {
BankAccount bankAccount = null;
BankAccountFactory bankAccountFactory = new BankAccountFactory();
Scanner in = new Scanner(System.in);
System.out.println("Please enter\n" +
" P for Personal account\n" +
" B for Business account\n" +
" C for Checking account\n" +
"----------------------------");
String accountType = in .nextLine();
bankAccount = bankAccountFactory.createAccount(accountType);
bankAccount.registerAccount();;
}
}
If we run our code, we will have the following output:
Please enter
P for Personal account
B for Business account
C for Checking account
----------------------------
P
Creating a personal account
Please enter
P for Personal account
B for Business account
C for Checking account
----------------------------
B
Creating a business account
Please enter
P for Personal account
B for Business account
C for Checking account
----------------------------
C
Creating a checking account
Here is the complete test case:
package com.javadevjournal.designpatterns.factory;
import com.javadevjournal.designpatterns.factory.account.BankAccount;
import com.javadevjournal.designpatterns.factory.account.BusinessAccount;
import com.javadevjournal.designpatterns.factory.account.CheckingAccount;
import com.javadevjournal.designpatterns.factory.account.PersonalAccount;
import org.junit.Before;
import org.junit.Test;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
public class FactoryDesignPatternTest {
private BankAccountFactory factory;
@Before
public void setUp() {
factory = new BankAccountFactory();
}
@Test
public void testPersonalAccount() {
BankAccount account = factory.createAccount("P");
assertThat(account, instanceOf(PersonalAccount.class));
}
@Test
public void testBusinessAccount() {
BankAccount account = factory.createAccount("B");
assertThat(account, instanceOf(BusinessAccount.class));
}
@Test
public void testCheckingAccount() {
BankAccount account = factory.createAccount("C");
assertThat(account, instanceOf(CheckingAccount.class));
}
@Test
public void testValidAccountType() {
BankAccount account = factory.createAccount("X");
assertNull(account);
}
}
Here is how the overall class diagram look like:
3. When to use Factory Design Pattern
- We can use the factory design pattern when we don’t know the type before hand. In our example, we don;t know the type of account customer may choose with our bank. We are creating the account type at run time based on customer input.
- When we want to extend the code to support more implementation in the future. We can always launch new account type in future by adding a smaller code in the factory class without touching any other code.
- When we want to reuse the same objects to the same system resources.
- Factory design pattern help us separating the object creation code from the one which is using it.
- We can use the Factory Method when we want to provide users of our library with a way to extend its internal components.
4. Advantages of Factory Design Pattern
- Factory design pattern removes the actual class initiation from client classes. So, it is clear use case abstraction.
- Factory method supports SOLID principles and it allows the developers to code for interface instead of implementation.
- It ensures that adding new implementation is super-easy and we can just another shape like pentagon very easily and add a couple of lines in Factory code to support that.
- Design pattern makes the entire code expandable and future-ready.
- Factory design pattern makes code highly cohesive and less coupled.
Keep in mind that it will add some complexity to our code base we we have to implement a new subclass for each new account type (But this is good in long term and code maintenance 🙂 )
5. Example in JDK and Spring
Here are some example where this is used in JDK and Spring Framework.
- Spring Framework
BeanFactory
- Spring Framework
ApplicationContext
java.util.Calendar#getInstance()
java.text.NumberFormat#getInstance()
java.util.EnumSet#of()
java.util.Collection#Iterator
Summary
In this article we talked about the factory design pattern and it’s implementation using Java. We talked about it’s benefits and when we should use this design pattern in our application. You can download the source code from our GitHub Repository.