Reduce conditional statements using Factory Method Pattern : JAVA
Writing more modular, maintainable and optimised solution is one of the most important skill to have as a software engineer.
JAVA gives us the ability to address inefficiency through different design patterns. We’ll be discussing about one such design pattern . The Factory method design pattern is a creational pattern that provides an interface for creating objects in a superclass, but allows subclasses to alter the type of objects that will be created. It is used to encapsulate object creation logic and promote loose coupling by creating objects without exposing the instantiation logic to the client.
Let’s checkout a special case where Factory method design pattern shows it’s magic.
Situation
We have a JAVA class where we are calling different operation functions depending upon the conditions given.
public class PerformOperations{
public void performOperation(ObjectModel objectModel) throws Exception{
if (objectModel.getName()=="new_name"){
call_new_name(objectModel);
}
else
if (objectModel.getName()=="old_name"){
call_old_name(objectModel);
}
else
if (objectModel.getName()=="deleted_name"){
call_deleted_name(objectModel);
}
}
}
Analysis
There are two problems with this design:
1. If in future more conditions arrive e.g. - objectModel.getName() == “append_name”, we’ll have to amend the code with the necessary if-condition and corresponding code/ function call. This will increase the code size, making it less readable.
2. We are calling different operation functions with same parameters, making it a bit redundant.
Solution
In order to reduce the if/else condition statements and enhance readability, we can introduce the a factory class which can help us to generate the instances of nameOperator classes.
The nameOperator class instances can be mapped using the unique identifier parameter which is currently getting used in if/else conditions (objectModel name) in the OperatorFactory class.
The NameOperator classes can be an implementation of NameOperations interface where we define the common nameOperation methods.
The PerformOperations class will now call the Factory Class to map the desired NameOperation class implementation.
Implementation
//NameOperations interface : nameOperation method
public interface NameOperations {
void nameOperation(ObjectModel objectModel) throws Exception;
}
//Operator Factory Class
public class OperatorFactory {
static Map<String, NameOperations> nameOperationMap = new HashMap<>();
static {
nameOperationMap.put("new_name", new NewNameOperation());
nameOperationMap.put("old_name", new OldNameOperation());
nameOperationMap.put("deleted_name", new DeletedNameOperation());
}
public Optional<NameOperations> getNameOperation(String nameOperator) {
return Optional.ofNullable(nameOperationMap.get(nameOperator));
}
}
//NameOperator class
public class NewNameOperation implements NameOperations {
void nameOperation(ObjectModel objectModel) throws Exception{
System.out.println("New name operation");
return;
}
}
//PerformOperations class
public class PerformOperations{
public void performOperation(ObjectModel objectModel) throws Exception{
Operations targetNameOperation = NameOperatorFactory
.getNameOperation(objectModel.getName().toString())
.orElseThrow(() -> new IllegalArgumentException("Invalid NameOperator"));
targetNameOperation.nameOperation(objectModel);
}
}
The above implementation can help us remove the if/else conditioning meanwhile enhancing modularity and readability of the code. If in future a new condition arrives, we can easily implement if with minimal changes in Factory class and NameOperation class for the same.
Thanks for reading, happy coding :)
More to read?
https://www.geeksforgeeks.org/factory-method-design-pattern-in-java/