Factory Pattern

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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class Point
{
private double _x, _y;

/// <summary>
/// Initializes a point from EITHER cartesian or polar
/// </summary>
/// <param name="a">x if cartesian, rho if polar</param>
/// <param name="b">y if cartesian, theta if polar</param>
/// <param name="cs">Coordinate system enum.</param>
public Point(double a, double b, CoordinateSystem cs = CoordinateSystem.Cartesian)
{
switch (cs)
{
case CoordinateSystem.Polar:
_x = a * Math.Cos(b);
_y = a * Math.Sin(b);
break;
default:
_x = a;
_y = b;
break;
}
}
}

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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class Point
{
private double _x, _y;

// this is a factory method
public static Point NewCartesianPoint(double x, double y)
{
return new Point(x, y);
}

// this is a factory method
public static Point NewPolarPoint(double rho, double theta)
{
return new Point(
rho * Math.Cos(theta),
rho * Math.Sim(theta));
}

private Point(double x, double y)
{
_x = x;
_y = y;
}
}

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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public PointFactory
{
public Point NewCartesianPoint(double x, double y)
{
return new Point(x, y);
}

public Point NewPolarPoint(double rho, double theta)
{
return new Point(
rho * Math.Cos(theta),
rho * Math.Sim(theta));
}
}

public class Point
{
private double _x, _y;

public Point(double x, double y)
{
_x = x;
_y = y;
}
}

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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public static class Point
{
private double _x, _y;

public static class Factory
{
public static Point NewCartesianPoint(double x, double y)
{
return new Point(x, y);
}

public static Point NewPolarPoint(double rho, double theta)
{
return new Point(
rho * Math.Cos(theta),
rho * Math.Sim(theta));
}
}

private Point(double x, double y)
{
_x = x;
_y = y;
}
}

// then call as
var point = Point.Factory.NewCartesianPoint(0, 0);

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
2
3
4
5
public interface IPriceRule
{
bool IsMatch(OrderItemModel item);
decimal CalculatePrice(OrderItemModel item);
}

Create the interface for the calculator ShoppingCart/Interfaces/IPricingCalculator.cs

1
2
3
4
public interface IPricingCalculator
{
decimal CalculatePrice(OrderItemModel item);
}

Implement some rules ShoppingCart/Business/PriceRules/EachPriceRule.cs

1
2
3
4
5
6
7
8
9
10
11
12
public class EachPriceRule : IPriceRule
{
public bool IsMatch(OrderItemModel item)
{
return item.Sku.StartsWith("EACH");
}

public decimal CalculatePrice(OrderItemModel item)
{
return item.Quantity * 5m;
}
}

Implement the calculator PricingCalculator.cs, the IEnumerable<IPriceRule> would be injected using dependency injection.

  • NOTE: If the calculator returned IPriceRule instead of the decimal it would be a true factory, calling .First is still retrieving an IPriceRule so the pattern is still the same. If you look at the Hot Drink Machine example below it does return the rules interface.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class PricingCalculator : IPricingCalculator
{
private readonly IEnumerable<IPriceRule> _pricingRules;

public PricingCalculator(IEnumerable<IPriceRule> pricingRules)
{
_pricingRules = pricingRules;
}

public decimal CalculatePrice(OrderItemModel item)
{
return _pricingRules
.First(r => r.IsMatch(item))
.CalculatePrice(item);
}
}

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:

  1. Create the interface IHotDrink
  2. Create the implementations, here internal insinuates we wont be giving out these classes but rather the IHotDrink
  3. Create factory interface IHotDrinkFactory
  4. 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 the IHotDrinkFactory.
  5. Create the HotDrinkMachine class, there are two ways he loaded them up
    1. Dictionary from enumeration AvailableDrink that maps to available factories
    2. HotDrinkMachine using reflection, typically you would use DI to inject these
  6. Finally this can be called as
1
2
3
var machine = new HotDrinkMachine();
IHotDrink drink = machine.MakeDrink(); // 1st example had params here
drink.Consume();

References