In the previous post I had mentioned about Factory Design Pattern. We already had an implementation alongside the Strategy Design pattern. In this post I would like to talk in detail about the Factory Design Pattern. The primary use of factory pattern is to encapsulate the object creation logic from the client. Factory pattern is a creational pattern which helps decoupling the consumers of a class from creating a new instance. Every time we use a new keyword we are tightly coupling the class and its consumer. Factory pattern is also useful when we have a common interface from which we can choose one of the option to create an instance.
Problem Statement
Assume that we are building software for a bank. Customers of the bank are entitled to receive the account statement. They can choose the mode of delivery for the statement. It can be a physical statement or an electronic account statement. Based on the customer preference the account statement will be delivered to the customer either in physical paper format or an electronic format in an email.
Solution
Similar to the previous post, I don’t intend to use any user interface for this post. Here is a simple AccountStatement class which has a method called DispatchStatement. Based on the input parameter dispatch mode we construct either the PhysicalStatementDispatcher or ElectronicStatementDispatcher instance.
public class AccountStatement
{
public void DispatchStatement(DispatchMode dispatchMode)
{
IStatementDispatcher dispatcher = null;
if (dispatchMode.Equals(DispatchMode.Physical))
{
dispatcher = new PhysicalStatementDispatcher
{
UnitNumber = 101,
BuildingNumber = 100,
Street = "Little India",
PostalCode = 123456
};
}
else if (dispatchMode.Equals(DispatchMode.Electronic))
{
dispatcher = new ElectronicStatementDispatcher
{
Email = "[email protected]",
AlternateEmail = "[email protected]"
};
}
if (dispatcher != null)
{
dispatcher.Dispatch();
}
}
}
The settings varies based on the type of dispatcher. For the physical statement we specify the details related to the postal address. For the Electronic statement we specify the email address. Finally the Dispatch method is called on the dispatcher instance to dispatch the statement. For simplicity I have hardcoded the values. In real applications these values would come from some persistent data store like database.
Problem with this approach
The problem with this approach are similar to the ones with the previous example related to Strategy pattern. The SRP as well as OCP is violated. The DispatchStatement method contains more than one responsibility. This method knows way too many things like the email address and the postal address of the customer which are not really necessary. Lets try to refactor the solution towards using the Factory pattern. The Factory pattern is a creational pattern which is used to encapsulate the creational logic. In our case we are creating an instance of the dispatcher based on the type of dispatch mode.
Refactored Solution
The refactored solution is much simpler and easier to implement.
public class AccountStatement
{
public void DispatchStatement(DispatchMode dispatchMode)
{
IStatementDispatcher dispatcher = StatementDispatcherFactory.CreateDispatcher(dispatchMode);
if (dispatcher != null)
{
dispatcher.Dispatch();
}
}
}
The complete portion of creation of the dispatcher has been moved to the factory. DispatchStatement method delegates the call to CreateDispatcher by passing the dispatch mode. The StatementDispatcherFactory is very simple with just the same code moved from AccountStatement class.
public static class StatementDispatcherFactory
{
public static IStatementDispatcher CreateDispatcher(DispatchMode dispatchMode)
{
IStatementDispatcher dispatcher = null;
if (dispatchMode == DispatchMode.Physical)
{
dispatcher = new PhysicalStatementDispatcher
{
UnitNumber = 101,
BuildingNumber = 100,
Street = "Little India",
PostalCode = 123456
};
}
else if (dispatchMode == DispatchMode.Electronic)
{
dispatcher = new ElectronicStatementDispatcher
{
Email = "[email protected]",
AlternateEmail = "[email protected]"
};
}
return dispatcher;
}
}
By moving the creational logic to the factory we have made the AccountStatement class extensible. We can add more number of dispatchers in future without modifying the AccountStatment class. The OCP is satisfied by moving the creational logic to the factory.
The other advantage of using the factory is the client need not be aware of the complex construction process. In future we can change the implementation of the one or more dispatcher without impacting the client. Say for example the PhysicalStatementDispatcher is currently using the postal service to deliver the statement and decides to change and use a low cost courier service for delivery of physical statements. This change can be implemented without modifying the AccountStatement class. The account statement class does not need to know who is the provider for the courier service.
Conclusion
Factories are very useful when certain decisions are to be made at runtime based on user preferences or environment settings. By encapsulating the creational logic in a central location we can ensure that changes are minimal in the client code. This is very useful when constructing business entities which can be used in multiple places throughout an application. Say for example a business entity is used in 50 places within an application. Due to some new enhancement the constructor parameters needs to be changed. Adding or removing the constructor parameter will impact all those 50 places. We can minimise this change by using factory and centralising the object creation logic within the factory. By just changing the implementation within the factory we can ensure that all the client requests are satisfied.
While I was working with Tesco, our team had made good use of Factory pattern on almost all the projects that I was involved in. The factories were also helpful during unit testing as they allowed us to return mock instances of the classes.
As always the complete working solution is available for download.
Until next time Happy Programming.
Further Reading
Here are some book I recommend related to the topics discussed in this blog post.