Motivation & Definition
A factory component’s responsibility is solely for the wholesale creation of objects so non-piecewise like a builder pattern. Motivational usage of a factory could include:
- Object creation logic sometimes becomes too convoluted
- Constructor is not descriptive, its name will match the name of the containing type and additional information about how the object is constructed would need to be communicated in the summary documentation above the method. You cannot overload with same sets of arguments with different names. Optional parameters are confusing and not a great idea for a constructor.
- Entire object creation (so non-piecewise like a builder pattern) is outsourced to separate function (factory method), may exist in a separate class (factory), hierarchy of factories (abstract factory)
Point Example from Dmitri Nesteruk of a constructor that can be simplified by use of a factory.
1 | public class Point |
Factory Method
The factory method is used to replace class constructors, abstracting the process of object generation so that the type of the object instantiated can be determined at run-time.
Refactoring the example above to use factory methods:
1 | public class Point |
Factory
A factory is a separate component that knows how to initialize types in a particular way.
It can be argued that the factory methods above violates SRP (Single Responsibility Principle) as creation of the object and what it actually does are different concerns. You can move the factory methods into their own class.
A caveat however would then be that the constructor now needs to be public and the consumer can access it. If your assembly was being downloaded as a package (Example: Nuget) you can mark the constructor as internal which will fix this problem however this is not always the case.
1 | public PointFactory |
Building on the above you can solve the public constructor issue by using an inner factory which would simply put the factory back inside Point as an inner class. They don’t have to be static but its the simplest.
This is how some of the .net framework is written, example: Task.Factory.StartNew ...
1 | public static class Point |
Abstract Factory
The abstract factory pattern is used to provide a client with a set of related or dependent objects. The “family” of objects created by the factory are determined at run-time. These objects can be interfaces or abstract classes.
This is relatively rarely used outside of a large complicated code bases however smaller contrived examples can be explained using a simple shopping cart which follows OCP (Open Close Principle).
Cart Example
This is a cart example based on code done by Steve Smith aka Ardalis
Create the interface for the rules ShoppingCart/Interfaces/IPriceRule.cs
1 | public interface IPriceRule |
Create the interface for the calculator ShoppingCart/Interfaces/IPricingCalculator.cs
1 | public interface IPricingCalculator |
Implement some rules ShoppingCart/Business/PriceRules/EachPriceRule.cs
1 | public class EachPriceRule : IPriceRule |
Implement the calculator PricingCalculator.cs, the IEnumerable<IPriceRule>
would be injected using dependency injection.
- NOTE: If the calculator returned
IPriceRule
instead of thedecimal
it would be a true factory, calling.First
is still retrieving anIPriceRule
so the pattern is still the same. If you look at theHot Drink Machine
example below it does return the rules interface.
1 | public class PricingCalculator : IPricingCalculator |
Then to consume inject IPricingCalculator as _pricingCalculator
, its CalculatePrice
method will find the correct rule from the list based on item and then fluently call the rules CalculatePrice
method.
1 | _pricingCalculator.CalculatePrice(item); |
Hot Drink Machine Example
A HotDrinkMachine
example from Dmitri Nesteruk explains that an abstract factory doesn’t necessarily need to return a family of objects but can return different objects with their own separate factories. This can be expanded to return families of objects per the text book definition.
The code creation would be done as follows:
- Create the interface
IHotDrink
- Create the implementations, here
internal
insinuates we wont be giving out these classes but rather theIHotDrink
- Create factory interface
IHotDrinkFactory
- Create the factory, the assumption here is the process for making Tea and Coffee is vastly different and therefor warrants their own factory. So we will create two factory objects. Again, here
internal
insinuates we wont be giving out these classes but rather theIHotDrinkFactory
. - Create the
HotDrinkMachine
class, there are two ways he loaded them up - Finally this can be called as
1 | var machine = new HotDrinkMachine(); |
References
- https://dzone.com/articles/factory-method-vs-abstract
- https://www.geeksforgeeks.org/abstract-factory-pattern/
- http://www.blackwasp.co.uk/gofpatterns.aspx
- https://www.dotnettricks.com/learn/designpatterns/factory-method-design-pattern-dotnet
- https://www.c-sharpcorner.com/article/factory-method-design-pattern-in-c-sharp/