Factory Design patterns are Creational patterns and deals with how the objects are created. There are three design patterns, namely, Simple Factory, Factory Method and Abstract Factory design patterns. All these patterns hide the object creation process from the client and supply the objects without specifying the exact class of object that will be created. These patterns are widely used across programming languages especially Java and .Net. In this discussion, we will see the different factory pattern implementations and validate them against open-closed principle.
Table of Contents
The Open-Closed Principle
Open-Closed principle (by Bertrand Meyer) is one of the fundamental principle of object oriented programming and is a one of the principle of SOLID.
According to Open-Closed Principle the “Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification. “
There are many different views on Open-Closed Principle and its applicability, scope and practicality. Even some argues that, it is closely related to Protected Variation pattern of General Responsibility Assignment Software Patterns (GRASP) (Reference: Applying UML and Patterns Craig Larman ). It says, protect elements from the variations of other elements by wrapping the focus of instability with an interface and using polymorphism to create various implementation of this interface.
Factory Design Patterns and Open-Closed Principles
The question is, when you design your program or module using Factory design patterns, are you violating the Open-Closed principle?
Let us check this by using sample programs of all the three Factory patterns.
As we mentioned above, Factory patterns are used to implement the concept of factories and deals with the problem of creating objects (exact class of object that will be created will not be specified) also called products. Factory pattern comes in different variants and implementations such as GoF’s Factory Method and Abstract Factory. First let us see the simple Factory pattern example. We will see the Factory Method and Abstract Factory patterns after this.
Simple Factory Pattern
The simple Factory (method) pattern is really simple. The client requires a Product object and instead of creating the product using the new operator, it asks the Factory for a new object. The client also provides the information on which type of Product it is looking for. As with all factory pattern clients, the client here use the product object as abstract product without being aware of the concrete product implementation.
As mentioned in Head First Design Patterns, the simple factory is not actually a design pattern, its more of a programming idiom.
See the Java example below.
The Product Interface
/* * @(#)Vehicle.java * @author Binu George * Globinch.com * copyright https://www.globinch.com. All rights reserved. */ package com.globinch.pattern.factory.simple; /** * Product interface. * * @author Binu George * @since 2013 * @version 1.0 */ public interface Vehicle { public abstract void drive(); }
Product Concrete classes
/* * @(#)Car.java * @author Binu George * Globinch.com * copyright https://www.globinch.com. All rights reserved. */ package com.globinch.pattern.factory.simple; /** * Car -One of concrete product class. * * @author Binu George * @since 2013 * @version 1.0 */ public class Car implements Vehicle { /** * */ public Car() { // TODO Auto-generated constructor stub } /* (non-Javadoc) * @see com.globinch.pattern.factory.simple.Vehicle#drive() */ @Override public void drive() { System.out.println("I am driving a car!"); } }
/* * @(#)Truck.java * @author Binu George * Globinch.com * copyright https://www.globinch.com. All rights reserved. */ package com.globinch.pattern.factory.simple; /** * Truck -One of concrete product class. * * @author Binu George * @since 2013 * @version 1.0 */ public class Truck implements Vehicle { /** * */ public Truck() { // TODO Auto-generated constructor stub } /* (non-Javadoc) * @see com.globinch.pattern.factory.simple.Vehicle#drive() */ @Override public void drive() { System.out.println("I am driving a truck !!"); } }
The Factory class
/* * @(#)VehicleFactory.java * @author Binu George * Globinch.com * copyright https://www.globinch.com. All rights reserved. */ package com.globinch.pattern.factory.simple; /** * Vehicle Factory class. * * @author Binu George * @since 2013 * @version 1.0 */ public enum VehicleFactory { INSTANCE; /** * */ public Vehicle createVehicle(String vehicleType)throws Exception{ if(vehicleType.equalsIgnoreCase("car")){ return new Car(); }else if(vehicleType.equalsIgnoreCase("truck")){ return new Truck(); } throw new Exception("The vehicle type is unknown!"); } }
We are making the Factory as a singleton class. We are using singletons with a single-element enum type and is currently the best way to implement a singleton in Java 1.6. (Effective Java from Joshua Bloch).
Test our factory implementation
/* * @(#)TestDrive.java * @author Binu George * Globinch.com * copyright https://www.globinch.com. All rights reserved. */ package com.globinch.pattern.factory.simple; /** * @author binugeorge * */ public class TestDrive { /** * Simple Factory test class. * * @author Binu George * @since 2013 * @version 1.0 */ public TestDrive() { // TODO Auto-generated constructor stub } /** * @param args */ public static void main(String[] args) { try { VehicleFactory factory = VehicleFactory.INSTANCE; Vehicle vehicle = factory.createVehicle("car"); vehicle.drive(); vehicle = factory.createVehicle("truck"); vehicle.drive(); vehicle = factory.createVehicle("truck1"); vehicle.drive(); } catch (Exception e) { System.out.println(e.getMessage()); } } }
I am driving a car! I am driving a truck !! The vehicle type is unknown!
The problem with the above factory implementation is that, once we add a new concrete Vehicle, we should modify the Factory class to accommodate that. This violates the open-closed principle.
Can we fix the above example to satisfy the open-closed principle? Let us see the example below. Here we just use one extra Enum class to hold the name of the new Vehicle concrete classes. The VehicleFactory is modified to load it dynamically. But please note that the package location we used to load the concrete classes dynamically. You can configure this location as a property.
/* * @(#)VehicleTypes.java * @author Binu George * Globinch.com * copyright https://www.globinch.com. All rights reserved. */ package com.globinch.pattern.factory.simple.ocp; /** * VehicleTypes enum. * * @author Binu George * @since 2013 * @version 1.0 */ public enum VehicleTypes { Car,Truck }
Modified Factory class
/* * @(#)VehicleFactory.java * @author Binu George * Globinch.com * copyright https://www.globinch.com. All rights reserved. */ package com.globinch.pattern.factory.simple.ocp; import java.util.HashMap; import java.util.Map; /** * VehicleFactory. * * @author Binu George * @since 2013 * @version 1.0 */ public enum VehicleFactory { INSTANCE; private Mapvehicles = new HashMap (); /** * Method to create vehicle types * @param vehicleType * @return Vehicle * @throws Exception */ public Vehicle createVehicle(String vehicleType) throws Exception { Vehicle vehicle = vehicles.get(vehicleType); if (vehicle != null) { return vehicle; } else { try { String name = Vehicle.class.getPackage().getName(); vehicle = (Vehicle) Class.forName(name+"."+vehicleType).newInstance(); vehicles.put(vehicleType, vehicle); return vehicle; } catch (Exception e) { throw new Exception("The vehicle type is unknown!"); } } } }
Test the factory implementation
/* * @(#)TestDrive.java * @author Binu George * Globinch.com * copyright https://www.globinch.com. All rights reserved. */ package com.globinch.pattern.factory.simple.ocp; /** * Test VehicleFactory. * * @author Binu George * @since 2013 * @version 1.0 */ public class TestDrive { /** * */ public TestDrive() { // TODO Auto-generated constructor stub } /** * Main test method * @param args */ public static void main(String[] args) { try { VehicleFactory factory = VehicleFactory.INSTANCE; Vehicle vehicle = factory.createVehicle(VehicleTypes.Car.name()); vehicle.drive(); vehicle = factory.createVehicle(VehicleTypes.Truck.name()); vehicle.drive(); vehicle = factory.createVehicle(VehicleTypes.Car.name()); vehicle.drive(); vehicle = factory.createVehicle("truck1"); vehicle.drive(); } catch (Exception e) { System.out.println(e.getMessage()); } } }
I am driving a car! I am driving a truck !! I am driving a car! The vehicle type is unknown!
In the above example, you don’t have to touch any of the Framework classes, if you are adding a new VehicleType. You just need to add the new VehicleType entry in the Vehicle type enum class, which is used by only the client class.
Now, the above example is more or less follows the Open-closed principle. New vehicle types can be created as per demand and you don’t have to touch the Factory to create instances.
Factory Method Pattern – GoF Factory pattern
The classic definition of Factory Method (also known as Virtual Constructor) pattern is “Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses.”
This is one of the widely used patterns in Java and .NET. One example in Java is java.net.ContentHandlerFactory interface. This interface defines a factory for content handlers. URLConnection.getContent() method uses this factory to get the appropriate content object.
This pattern is used, when the class wants its subclasses to specify the object it creates and when the class cannot anticipate the object class it needs to create. That means, the client doesn’t know the type of the objects that needs to be created, still want to perform operations on them. Here the creator or factory relies on its subclass’s factory method to return instance of an appropriate concrete product object. The creator can execute some operations or sequence of operations on the object.
This pattern decouples product sub class details from the client and new products can be added without affecting the existing code.
See the example below.
Abstract Product Base class
/* * @(#)Car.java * @author Binu George * Globinch.com * copyright https://www.globinch.com. All rights reserved. */ package com.globinch.pattern.factorymethod; /** * Base class. * * @author Binu George * @since 2013 * @version 1.0 */ public abstract class Car { private String make; public Car(String make){ this.make = make; } public abstract void drive(); /** * @return the make */ public String getMake() { return make; } /** * @param make the make to set */ public void setMake(String make) { this.make = make; } }
Concrete Product classes
/* * @(#)HatchBack.java * @author Binu George * Globinch.com * copyright https://www.globinch.com. All rights reserved. */ package com.globinch.pattern.factorymethod; /** * HatchBack. * * @author Binu George * @since 2013 * @version 1.0 */ public class HatchBack extends Car { /** * */ public HatchBack(String make) { super(make); } /* (non-Javadoc) * @see com.globinch.pattern.factorymethod.Car#drive() */ @Override public void drive() { System.out.println("I am driving "+getMake()+" hatchback !"); } }
/* * @(#)Sedan.java * @author Binu George * Globinch.com * copyright https://www.globinch.com. All rights reserved. */ package com.globinch.pattern.factorymethod; /** * Sedan. * * @author Binu George * @since 2013 * @version 1.0 */ public class Sedan extends Car { /** * */ public Sedan(String make) { super(make); } /* (non-Javadoc) * @see com.globinch.pattern.factorymethod.Car#drive() */ @Override public void drive() { System.out.println("I am driving "+getMake()+" Sedan car!"); } }
A utility enum class, in which we define all implemented product types.
/* * @(#)CarMake.java * @author Binu George * Globinch.com * copyright https://www.globinch.com. All rights reserved. */ package com.globinch.pattern.factorymethod; /** * CarMake. * * @author Binu George * @since 2013 * @version 1.0 */ public enum CarMake { Honda, Toyota, Ford, Renault }
Abstract Factory base class – Creator class
The Creator class specifies all standard and generic behavior of the products and when a new product is needed, it sends the creation details that are supplied by the client to the ConcreteCreator. Ideally the Creator class is abstract and the product generation method is abstract. It is the responsibility of the concrete creator classes to implement the product generation logic. Sometimes, even the creator class can have default product creation method implementation and the concrete classes may override the method if required.
/* * @(#)CarFactory.java * @author Binu George * Globinch.com * copyright https://www.globinch.com. All rights reserved. */ package com.globinch.pattern.factorymethod; /** * CarFactory. * * @author Binu George * @since 2013 * @version 1.0 */ public abstract class CarFactory { public Car getCar(String make) throws Exception { try{ CarMake.valueOf(make); }catch (IllegalArgumentException e) { throw new Exception("This make " + make + " is not available"); } Car car = manufactureCar(make); System.out.println("...The car " + car.getMake()+ " is ready to deliver..."); return car; } protected abstract Car manufactureCar(String make); }
Concrete Factory classes.
/* * @(#)HatchBackFactory.java * @author Binu George * Globinch.com * copyright https://www.globinch.com. All rights reserved. */ package com.globinch.pattern.factorymethod; /** * HatchBackFactory. * * @author Binu George * @since 2013 * @version 1.0 */ public class HatchBackFactory extends CarFactory { /** * */ public HatchBackFactory() { // TODO Auto-generated constructor stub } /* (non-Javadoc) * @see com.globinch.pattern.factorymethod.CarFactory#manufactureCar(java.lang.String) */ @Override public Car manufactureCar(String make) { return new HatchBack(make); } }
/* * @(#)SedanFactory.java * @author Binu George * Globinch.com * copyright https://www.globinch.com. All rights reserved. */ package com.globinch.pattern.factorymethod; /** * SedanFactory. * * @author Binu George * @since 2013 * @version 1.0 */ public class SedanFactory extends CarFactory { /* (non-Javadoc) * @see com.globinch.pattern.factorymethod.CarFactory#manufactureCar(java.lang.String) */ @Override public Car manufactureCar(String make) { return new Sedan(make); } }
Test class
/* * @(#)TestCarFactory.java * @author Binu George * Globinch.com * copyright https://www.globinch.com. All rights reserved. */ package com.globinch.pattern.factorymethod; /** * TestCarFactory. * * @author Binu George * @since 2013 * @version 1.0 */ public class TestCarFactory { /** * */ public TestCarFactory() { // TODO Auto-generated constructor stub } /** * @param args */ public static void main(String[] args) { try { CarFactory carFactory =new SedanFactory(); Car car = carFactory.getCar(CarMake.Ford.name()); car.drive(); carFactory = new HatchBackFactory(); car = carFactory.getCar(CarMake.Toyota.name()); car.drive(); car = carFactory.getCar("Audi"); car.drive(); } catch (Exception e) { System.out.println(e.getMessage()); } } }
...The car Ford is ready to deliver... I am driving Ford Sedan car! ...The car Toyota is ready to deliver... I am driving Toyota hatchback ! This make Audi is not available
Output
The factory method pattern follows the Open-Closed principle. The product and creators are closed for modification but can extend and create new products and concrete factories. Here, we depend on abstraction not on concrete implementations.
If the factory method needs to support multiple product types, then the factory method has to accept a parameter to decide which concrete product object to create. If at any later point in future, if the client wants to add support for a new concrete product, then the programmer has to subclass the Creator class just to instantiate the new concrete product.
Abstract Factory Design Pattern
Abstract Factory patterns are other factory patterns where the Abstract factory is a super factory which creates other factories. The patterns offers interface for creating a family of related objects or a group of related objects without specifying the types. Here, again the client is always dealing with the abstract type.
Here, we will use the popular GUI Factory to see how to implement the abstract factory pattern.
The Product Interfaces.
The client is going to use only this interface when dealing with concrete objects which is hidden for client.
/* * @(#)Button.java * @author Binu George * Globinch.com * copyright https://www.globinch.com. All rights reserved. */ package com.globinch.pattern.abfactory; /** * Button. * * @author Binu George * @since 2013 * @version 1.0 */ public interface Button { public abstract void initialize(); public abstract void draw(); }
/* * @(#)TextBox.java * @author Binu George * Globinch.com * copyright https://www.globinch.com. All rights reserved. */ package com.globinch.pattern.abfactory; /** * TextBox. * * @author Binu George * @since 2013 * @version 1.0 */ public interface TextBox { public abstract void initialize(); public abstract void draw(); }
The Product implementation classes.
Here we create two families of objects.
/* * @(#)WindowsButton.java * @author Binu George * Globinch.com * copyright https://www.globinch.com. All rights reserved. */ package com.globinch.pattern.abfactory; /** * WindowsButton. * * @author Binu George * @since 2013 * @version 1.0 */ public class WindowsButton implements Button { /** * */ public WindowsButton() { // TODO Auto-generated constructor stub } /* (non-Javadoc) * @see com.globinch.pattern.abfactory.Button#draw() */ @Override public void draw() { System.out.println("Brilliant Windows Button ready."); } /* (non-Javadoc) * @see com.globinch.pattern.abfactory.Button#initialize() */ @Override public void initialize() { System.out.println("Initializing Windows Button"); } }
/* * @(#)WindowsTextBox.java * @author Binu George * Globinch.com * copyright https://www.globinch.com. All rights reserved. */ package com.globinch.pattern.abfactory; /** * WindowsTextBox. * * @author Binu George * @since 2013 * @version 1.0 */ public class WindowsTextBox implements TextBox { @Override public void draw() { System.out.println("Windows Text box ready."); } @Override public void initialize() { System.out.println("Windows Text box initailizing."); } }
/* * @(#)MacButton.java * @author Binu George * Globinch.com * copyright https://www.globinch.com. All rights reserved. */ package com.globinch.pattern.abfactory; /** * MacButton. * * @author Binu George * @since 2013 * @version 1.0 */ public class MacButton implements Button { /** * */ public MacButton() { // TODO Auto-generated constructor stub } /* (non-Javadoc) * @see com.globinch.pattern.abfactory.Button#draw() */ @Override public void draw() { System.out.println("Shining Mac Button is ready"); } /* (non-Javadoc) * @see com.globinch.pattern.abfactory.Button#initialize() */ @Override public void initialize() { System.out.println("Initializing Mac Button"); } }
/* * @(#)MacTextBox.java * @author Binu George * Globinch.com * copyright https://www.globinch.com. All rights reserved. */ package com.globinch.pattern.abfactory; /** * MacTextBox. * * @author Binu George * @since 2013 * @version 1.0 */ public class MacTextBox implements TextBox { /** * */ public MacTextBox() { // TODO Auto-generated constructor stub } /* (non-Javadoc) * @see com.globinch.pattern.abfactory.TextBox#draw() */ @Override public void draw() { System.out.println("Mac Textbox is ready."); } /* (non-Javadoc) * @see com.globinch.pattern.abfactory.TextBox#initialize() */ @Override public void initialize() { System.out.println("Mac Textbox initializing."); } }
Abstract Factory.
The concrete factories are responsible for creating concrete products.
/* * @(#)GUIFactory.java * @author Binu George * Globinch.com * copyright https://www.globinch.com. All rights reserved. */ package com.globinch.pattern.abfactory; /** * GUIFactory. * * @author Binu George * @since 2013 * @version 1.0 */ public interface GUIFactory { Button createButton(); TextBox createTextBox(); }
Concrete Factories.
/* * @(#)MacGUIFactory.java * @author Binu George * Globinch.com * copyright https://www.globinch.com. All rights reserved. */ package com.globinch.pattern.abfactory; /** * MacGUIFactory. * * @author Binu George * @since 2013 * @version 1.0 */ public class MacGUIFactory implements GUIFactory { /** * */ public MacGUIFactory() { // TODO Auto-generated constructor stub } /* (non-Javadoc) * @see com.globinch.pattern.abfactory.GUIFactory#createButton() */ @Override public Button createButton() { return new MacButton(); } /* (non-Javadoc) * @see com.globinch.pattern.abfactory.GUIFactory#createTextBox() */ @Override public TextBox createTextBox() { return new MacTextBox(); } }
/* * @(#)WindowsGUIFactory.java * @author Binu George * Globinch.com * copyright https://www.globinch.com. All rights reserved. */ package com.globinch.pattern.abfactory; /** * WindowsGUIFactory. * * @author Binu George * @since 2013 * @version 1.0 */ public class WindowsGUIFactory implements GUIFactory { /** * */ public WindowsGUIFactory() { // TODO Auto-generated constructor stub } /* (non-Javadoc) * @see com.globinch.pattern.abfactory.GUIFactory#createButton() */ @Override public Button createButton() { return new WindowsButton(); } /* (non-Javadoc) * @see com.globinch.pattern.abfactory.GUIFactory#createTextBox() */ @Override public TextBox createTextBox() { return new WindowsTextBox(); } }
The Factory creator who abstracts the way factories are instantiated.
/* * @(#)AbstractFactoryApp.java * @author Binu George * Globinch.com * copyright https://www.globinch.com. All rights reserved. */ package com.globinch.pattern.abfactory; /** * AbstractFactoryApp. * * @author Binu George * @since 2013 * @version 1.0 */ public class AbstractFactoryApp { /** * */ public AbstractFactoryApp() { // TODO Auto-generated constructor stub } public static GUIFactory getGUIFactory(String os) throws Exception{ try{ OperatingSystems.valueOf(os); }catch (IllegalArgumentException e) { throw new Exception("The OS "+os+" is not supported"); } GUIFactory factory = null; if(os.equalsIgnoreCase(OperatingSystems.WINDOWS.name())){ factory = new WindowsGUIFactory(); }else if(os.equalsIgnoreCase(OperatingSystems.MAC.name())){ factory = new MacGUIFactory(); } return factory; } }
The enum class of operating systems.
/* * @(#)OperatingSystems.java * @author Binu George * Globinch.com * copyright https://www.globinch.com. All rights reserved. */ package com.globinch.pattern.abfactory; /** * OperatingSystems. * * @author Binu George * @since 2013 * @version 1.0 */ public enum OperatingSystems { WINDOWS,MAC }
The test class.
/* * @(#)CarMake.java * @author Binu George * Globinch.com * copyright https://www.globinch.com. All rights reserved. */ package com.globinch.pattern.abfactory; /** * AbstractFactoryAppTest. * * @author Binu George * @since 2013 * @version 1.0 */ public class AbstractFactoryAppTest { /** * */ public AbstractFactoryAppTest() { // TODO Auto-generated constructor stub } /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub try { GUIFactory factory = AbstractFactoryApp.getGUIFactory(OperatingSystems.WINDOWS.name()); Button button = factory.createButton(); button.initialize(); button.draw(); TextBox textBox = factory.createTextBox(); textBox.initialize(); textBox.draw(); factory = AbstractFactoryApp.getGUIFactory(OperatingSystems.MAC.name()); button = factory.createButton(); button.initialize(); button.draw(); textBox = factory.createTextBox(); textBox.initialize(); textBox.draw(); factory = AbstractFactoryApp.getGUIFactory("Android"); button = factory.createButton(); } catch (Exception e) { System.out.println(e.getMessage()); } } }
Initializing Windows Button Brilliant Windows Button ready. Windows Text box initailizing. Windows Text box ready. Initializing Mac Button Shining Mac Button is ready Mac Textbox initializing. Mac Textbox is ready. The OS Android is not supported
Often methods of Abstract factory are implemented as factory methods. Factory method creates objects via inheritance while Abstract factory does it through object composition. If you want to add new concrete types, you can just modify the client code to use a different factory. This will not affect the any other classes in the module. In our above example, you may need to update the enumerator class to include the new operating system.
Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification. We should be able to extend software entities without actually modifying them. This is a crucial principle of SOLID and continuous integration. We usually apply this principle to OOP and we should apply it in every day javascript programming as well. The following video starting from the definition of this principle, identifies some common code smell and puts it into practice with a javascript example
Greate Article, it helps to clear my thinking about OCP and Factory DP.