Sometimes instantiation of an object requires more than just invoking a constructor. Indeed, we may need to perform certain order of initialization steps that are outside of object’s constructor responsibilities. In other cases, we may need to instantiate multiple dependencies and pass them as constructor parameters, or perhaps we need different types of objects based on some parameters, for example configuration files, environment, user settings and so on.
The idea behind factory design pattern is quite simple and it is to help instantiate objects when we require more than just a constructor call.
There are three flavors of factory pattern. The differences between them are quite subtle.
- Internal Factory
- Factory
- Factory Method
- Abstract Factory
We are going to look at each flavor from practical point of view. As an example, we will use domain model of a car manufacturer that has three models of cars on the market Sedan, SUV and Truck. Car manufacturer will use car factories to make cars. In our examples car factories will be implementations of different flavors of Factory design pattern.
Private Factory
Private Factory is usually not broadly discussed or defined, but I found it useful in many applications and decided to dedicate it a separate category. So, what is Private Factory and how do we use it? Let’s say we are given a class that needs to instantiate a complex object with multiple steps of initialization and configuration before the object can be used by our class. How do we approach this problem? We can start with very simple solution by implementing a private or protected Create*() method that does all the complex instantiation and configuration logic. We can use Create*() method every time we need new object. Private Factory is simple but quite powerful as it allows easy refactoring to more elaborate patterns if that is ever needed.
In the example below CarManufacturer class has private method CreateOrderedCar() that helps to create different models of cars based on an order. We can consider CreateOrderedCar() method as implementation of Private Factory pattern.
public class CarManufacturer { public Car MakeCar(Order order) { var car = CreateOrderedCar(order); // Some complicated logic that configured the car. // ... return car; } // Private Factory private Car CreateOrderedCar(Order order) { switch (order.Model) { case CarModel.Sedan: return new Sedan(); case CarModel.SUV: return new SUV(); case CarModel.Truck: return new Truck(); default: throw new Exception($"Unable to make {order.Model}"); } } }
In the example above, Private Factory pattern is implemented by CreateOrderedCar() method.
If we are not in OOP world, then Private Factory pattern can be implements as a create*() function that is available only within the modules where it is defined.
Factory
Factory is a class that helps to create one or more types of concrete objects. For that purpose, factory usually implements one or more Create*() methods, each of which returns an object of a concrete type.
We can look at simple life example of a car assembling factory. A car factory can produce multiple models of vehicles, sedan, SUV or truck. Factory is useful when our application needs to create and initialize same objects in multiple places. Factory encapsulates initialization complexity and provides simple and reusable interface to create new objects.
Example below shows CarFactory class that implements three methods that create different car models. CarFactory class is Factory pattern implementation.
public class CarFactory { public Sedan CreateSedan() { var sedan = new Sedan(); // Some complicated logic; // ... return sedan; } public SUV CreateSUV() { var suv = new SUV(); // Some complicated logic; // ... return suv; } public Truck CreateTruck() { var truck = new Truck(); // Some complicated logic; // ... return truck; } }
If we are not in OOP world, then Factory pattern can be implements as a set of Create*() functions that return concrete data structures that don’t have to have the same shape.
In the example above, Factory Method pattern is implemented by CarFactory base class and its child classes.
Factory Method
Factory Method is used to create objects of a concrete type from a hierarchy of objects and returns base object type.
If we use the domain model of car manufacturing, imagine that CarManufacturer needs to switch between car models it produces based on demand. To solve this problem we can create base CarFactory class that declares CreateCar() method which returns Car class.
public abstract class CarFactory { public abstract Car CreateCar(); }
Then we have a child SedanFactory class that only assembles sedans, so it will override CreateCar() and return SedanCar object. We also have a car factory that assembles SUVs which will also override CreateCar() but return an SUVCar object instead. Same for trucks, we have a TruckFactory that overrides CreateCar() and returns a TruckCar object.
public class SedanFactory : CarFactory { public override Car CreateCar() { return new Sedan(); } }
public class SUVFactory : CarFactory { public override Car CreateCar() { return new SUV(); } }
public class TruckFactory : CarFactory { public override Car CreateCar() { return new Truck(); } }
CarManufacturer uses SedanFactory, SUVFacotry and TruckFactory to create proper type of Car. We assume that the rest of car handling logic is the same. For example, before delivering a Car the CarManufacturer needs to test it and if the test succeeds if will send the car for delivery, otherwise will return it back for fixing.
public class CarManufacturer { public Car MakeCar(Order order) { var carFactory = SelectCarFactory(order); var car = carFactory.CreateCar(); if (!TestCar(car)) { throw new Exception("Return car for fixing"); } return car; } private bool TestCar(Car car) { // Elaborate car testing logic. // ... return true; } private CarFactory SelectCarFactory(Order order) { switch (order.Model) { case CarModel.Sedan: return new SedanFactory(); case CarModel.SUV: return new SUVFactory(); case CarModel.Truck: return new TruckFactory(); default: throw new Exception($"Unable to select factory for {order.Model}"); } } }
In the example above, Abstract Factory pattern is implemented by CarFactory base class and its child classes.
Factory Method pattern is useful when we need to create different types of objects that can be treated in common way. In other words, it’s not important for CarManufacturer whether it deals with SUV or Sedan or Truck, all of the cars go through the same testing process.
Abstract Factory
Abstract Factory is used when we need to create objects from different hierarchies that are somewhat related.
Returning back to our car factory example, imagine that the general sequence of steps to assemble a car remains the same, however the testing process for sedans, SUVs and trucks is slightly different now. How do we keep CarManufacturer code agnostic to the type of Car, but still be able to run corresponding post-assembly tests for different types of cars? One solution is to extract testing logic into corresponding CarTester classes e.g., SedanTester, SUVTester and TruckTester, and let CarManufacturer use it to test assembled cars. This approach though creates two parallel hierarchies of classes, Car and CarTester that are somewhat related. SedanTester can be used for testing of SedanCar objects but is incompatible with SUVCar and TruckCar objects.
public abstract class CarTester { public abstract bool TestCar(Car car); }
public class SedanTester : CarTester { public override bool TestCar(Car car) { if (car is Sedan sedan) { return TestSedan(sedan); } throw new NotSupportedException( "Only sedans are supported by the rester"); } private bool TestSedan(Sedan sedan) { // Elaborate car testing logic. // ... return true; } }
public class SUVTester : CarTester { public override bool TestCar(Car car) { if (car is SUV suv) { return TestSUV(suv); } throw new NotSupportedException( "Only SUVs are supported by the rester"); } private bool TestSUV(SUV suv) { // Elaborate car testing logic. // ... return true; } }
public class TruckTester : CarTester { public override bool TestCar(Car car) { if (car is Truck truck) { return TestTruck(truck); } throw new NotSupportedException( "Only Trucks are supported by the rester"); } private bool TestTruck(Truck truck) { // Elaborate car testing logic. // ... return true; } }
Now that we have satisfied the requirement to keep CarManufacturer code clean and independent of a Car type, who is going to be responsible for matching Car and CarTester types so that we don’t accidentally use TruckTester with SedanCar. This is where Abstract Factory comes to rescue. Let’s use the hierarchy of CarFactory classes, e.g. SedanFactory, SUVFactory and TruckFactory to create Car and corresponding Tester. Now CarFactory base class will declare two methods CreateCar() and CreateTester(). All child classes will override the methods and create and return corresponding objects.
public abstract class CarFactory { public abstract Car CreateCar(); public abstract CarTester CreateTester(); }
public class SedanFactory : CarFactory { public override Car CreateCar() { return new Sedan(); } public override CarTester CreateTester() { return new SedanTester(); } }
public class SUVFactory : CarFactory { public override Car CreateCar() { return new SUV(); } public override CarTester CreateTester() { return new SUVTester(); } }
public class TruckFactory : CarFactory { public override Car CreateCar() { return new Truck(); } public override CarTester CreateTester() { return new TruckTester(); } }
Now when CarManufacturer needs to produce certain type of car it just selects corresponding CarFactory and uses it for creating Car and CarTester objects.
public class CarManufacturer { public Car MakeCar(Order order) { var carFactory = SelectCarFactory(order); var car = carFactory.CreateCar(); var tester = carFactory.CreateTester(); if (!tester.TestCar(car)) { throw new Exception("Return car for fixing"); } return car; } private CarFactory SelectCarFactory(Order order) { ... } }
In the example above, Abstract Factory pattern is implemented by CarFactory base class and its child classes.
Summary
The table below summarizes all four flavors of Factory Design Pattern.
Private Factory | Factory | Factory Method | Abstract Factory | |
Implementation | Private method of a class that creates and returns object of a concrete type. | Factory class with public method(s) that create and return object(s) of a concrete type. | Hierarchy of Factory classes that create concrete objects but return base type (class or interface). | Hierarchy of Factory classes that create base class objects from somewhat related hierarchies. |
Return Type | Concrete type (class or interface). | Concrete type (class or interface). | Base type (class or interface). | Base types (classes or interfaces) of related hierarchies. |
Accessibility | Private or protected. | Internal or public. | Internal or public. | Internal or public. |
Usage | When we need to instantiate and initialize a complex object but still unsure if we want to expose the instantiation logic outside. | When we need to instantiate and initialize complex object or objects and reuse the instantiation logic in multiple classes. | When we need to instantiate complex object but all we interested in is a base type (class or interface). | When we need to instantiate complex objects from somewhat related hierarchies but all we interested in are base types (classes or interfaces). |
All code examples can be found on GitHub PavelHudau/BlogCodeExamples.
More design pattern article can be found here.
One thought on “Factory Design Pattern”