Factory Design Pattern – An Effective Approach
As you know the Factory Method Pattern or popularly known as the Factory Design Pattern is one the design patterns under the category of “Creational Design Pattern”. The basic principle behind the pattern is that at runtime we get an object of similar type based on the parameter we pass. There are many articles on this pattern and developers implement in various ways to achieve it. In this article I will show you how to create a better and most effective way of designing factory design pattern.
Technicalities
As I have told you we will get a similar type object at run-time in the case of a factory design so that the underlying implementation of an object will be behind the screen. Let us consider a simple approach. Let’s consider a Person object that can be either Male or Female. At runtime we should only consider the behavior of the person but not the gender. As the traditional approach we create a Person interface and we create two implementation classes like MalePerson and FemalePerson. Based upon the runtime gender data, we pass to a Factory method of a Factory class where we decide whether the gender type is Male or Female and accordingly we create the instance of the particular class and return the reference type object. This approach sounds good and we adopt it in many of our developmental activities. Can we ensure that it is the effective approach in the case of fine-grained multi-threaded applications. What about the performance? Is there any other approach? Yes we do.
Let’s consider another real time example. Think about a situation in an organization where an employee can be CEO, CTO, CFO, Developer, Test Engineer, HR, Personnel, Security etc. If you want to know the role of an employee based upon the organization, what will you do ? How will you create a better factory design so that we can easily find the role and there should not be performance penalty ? Will you adopt the same traditional approach by providing multiple if clauses ? You can make an argument that , we should use switch condition. Fine… Let us see traditional approach and let us measure the time.
Let us employ our factory design in a traditional manner.
package com.ddlab.rnd.patterns; /** * @author Debadatta Mishra(PIKU) * */ public interface Roles { public String getRole(); }
The above interface is used as a type as there can be various types of role in the organization. It has a method called “getRole()” that specifies the description of the role of the employee.
Let’s design the implementation classes for the suitable roles for CEO, CTO, and CFO in an organization.
package com.ddlab.rnd.patterns; /** * @author Debadatta Mishra(PIKU) * */ public class CEORoles implements Roles { public String getRole() { return "CEO is the supreme head of the company"; } } package com.ddlab.rnd.patterns; /** * @author Debadatta Mishra(PIKU) * */ public class CFORoles implements Roles { @Override public String getRole() { return "CFO is the finance head of a company"; } } package com.ddlab.rnd.patterns; /** * @author Debadatta Mishra(PIKU) * */ public class CTORoles implements Roles { @Override public String getRole() { return "CTO is the technology decision maker of a company"; } }
Now we have to think about the Factory from where we will the Object dynamically. Let us see the code below.
package com.ddlab.rnd.patterns; /** * @author Debadatta Mishra(PIKU) * */ public abstract class EmployeeFactory { public static Roles getRole( String type ) { Roles roles = null; if( type.equals("cfo")) roles = new CFORoles(); else if( type.equals("cto")) roles = new CTORoles(); else if( type.equals("ceo")) roles = new CEORoles(); return roles; } }
Let us write a simple test harness class to verify the design.
package com.ddlab.rnd.patterns; /** * @author Debadatta Mishra(PIKU) * */ public class TestTraditionalFactoryDesign { public static void main(String[] args) { String type = "ceo"; long startTime = System.nanoTime(); String role = EmployeeFactory.getRole(type).getRole(); System.out.println("Role ::: "+role); long endTime = System.nanoTime(); System.out.println("Time difference ::: "+(endTime-startTime)+" nano seconds"); } }
If you run the above program, the following is the output from my system.
For you information, my system has 4 GB RAM and I5 processor.
Role ::: CEO is the supreme head of the company Time difference ::: 3477574 nano seconds
The above design seems to be correct , but what about performance ? You may say it does not matter because it comes in terms of nano seconds. Of course it does not matter if your application is very small , but it really matters in case of large enterprise applications. if you are a good programmer or developer you can not ignore the performance issue, particularly in case of product development where ther may be similar products in the market.
To address the above issue, let us try another approach of factory design where there may be changes in the factory class.
Let us see the code below.
package com.ddlab.rnd.patterns; /** * @author Debadatta Mishra(PIKU) * */ public enum EmployeeType { CEO("CEO") { @Override public Roles getRoles() { return new CEORoles(); } }, CTO("CTO") { @Override public Roles getRoles() { return new CTORoles(); } }, CFO("CFO") { @Override public Roles getRoles() { return new CFORoles(); } }; private EmployeeType( String type ) { this.type = type; } private String type; public abstract Roles getRoles(); public String getType() { return type; } @Override public String toString() { return "TYPE CODE -> "+type; } }
The test harness class is given below.
package com.ddlab.rnd.patterns; import java.util.HashMap; import java.util.Map; /** * @author Debadatta Mishra(PIKU) * */ public class TestFactoryDesign { static Map<String,EmployeeType> typeMap = new HashMap<String,EmployeeType>(); static { typeMap.put("cto", EmployeeType.CTO); typeMap.put("ceo", EmployeeType.CEO); typeMap.put("cfo", EmployeeType.CFO); } public static void main(String[] args) { String empType = "ceo"; try { long startTime = System.nanoTime(); String whatIstheRole = typeMap.get(empType).getRoles().getRole(); System.out.println("Role of the Employee :::"+whatIstheRole); long endTime = System.nanoTime(); System.out.println("Time difference ::: "+(endTime-startTime)+" nano seconds"); } catch (NullPointerException e) { System.out.println("No such Role is found"); e.printStackTrace(); } } }
If you run the above code, you will get the following output.
Role ::: CEO is the supreme head of the company Time difference ::: 1049108 nano seconds
What about the time. Let use have comparision between the time taken in the traditional approach and the modern approach.
Traditional Approach | 3477574 nano seconds |
Modern Approach(using enum and Map) | 1049108 nano seconds |
Can you think about the time difference, it is just about 3 times faster than the traditional approach.
So which is better ? Of course the modern approach of using enum is better. Apart from enum, I have used Map to maintain the list of employee type and its corresponding enum. In this case there is no need to use if clause which may impact our performance as far cyclomatic complexity is concerned. It is always better to use the above approach of 1049108 nano seconds. You can use ConcurrentMap for your multi threaded application.
Conclusion
I hope you will enjoy my article on the factory design pattern. In case of any clarification you can contact me debadatta.mishra@gmail.com.
But here you have the type map at the test class, but it is out of the time measurement. What about the time taken for putting the objects in the map? It also should be taken into account right for time measurement!
Three times faster isn’t a significant difference. You have to find a way to evaluate your algorithm’s complexity. In terms of role counts, both of your algorithms are O(n), which is pretty good. What really matters is a difference in the algorithms complexity. For example, if you find a mechanism which is only O(ln(n)) in terms of role counts, you’re way better off, in terms of scalability.
Interesting approach.
Thanks for modern approach
I wonder how it would look your enum class when you have 20+ different enum values of which every single one has 5 methods. For example instead of just getRole() every has getPayRate(), getNumberOfHolidayDays(), .. and similar.. I have a feeling that it will grow really fast and be really difficult to maintain..