The Client no longer needs to know how to create an object or exactly what flavor of that class it will use
Simple Factory Example
We have a ShoppingCart Class and inside this Class we create a shippingProvider object. It will create different shippingProvider based on order’s sender country
1 2 3 4 5 6 7 8 9 10 11 12
if (order.Sender.Country == "Australia") { //Australia Post Shipping Provider } else if (order.Sender.Country == "Sweden") { //Swedish Postal Service Shipping Provider } else { throw new NotSupportedException("No shipping provider found for origin country"); }
But the shippingProvider object should not be created inside the ShoppingCart Class, ShoppingCart Class should just ask a ShippingProviderFactory Class for a shippingProvider object, and it will be provided one.
So we should moved the code to a new ShippingProviderFactory Class and invoke this class’s Creation method.
1
var shippingProvider = ShippingProviderFactory.CreateShippingProvider(order.Sender.Country);
One problem is not we are still hardcoding the Country inside our ShippingProviderFactory Class. We should add another layer of abstraction between the ShippingProviderFactory and the implementation of the ShippingProvider.
Factory Method
The Factory Method Pattern is introduced to allow for a flexible and extensible application
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
public abstract class ShippingProviderFactory { public abstract ShippingProvider CreateShippingProvider(string country);
public ShippingProvider GetShippingProvider(string country) { var provider = CreateShippingProvider(country)
// we may want to do some common changes on the shippingProvider created // before we return it back to the caller (ShoppingCart) if (country == "Sweden" && provider.InsuranceOptions.ProviderHasInsurance) { provider.RequireSignature = false; } return provider; } }
It contains two methods.
The CreateShippingProvider() method will be implemented by its subclasses with different implementations.
The GetShippingProvider() method will allow user to decide what’s passed into the creation. And it allows user to do additional common interactions with the result of the creation before it’s being passed back to the caller(ShoppingCart).
Now we can create different implementations of the creation of a shippingProvider based on the input parameter(country).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
public class StandardShippingProviderFactory : ShippingProviderFactory { public override ShippingProvider CreateShippingProvider(string country) { return new StandardShippingProviderFactory(); } }
public class GlobalExpressShippingProviderFactory : ShippingProviderFactory { public override ShippingProvider CreateShippingProvider(string country) { return new GlobalExpressShippingProvider(); } }
In the caller Class (ShoppingCart) we can inject ShippingProviderFactory
1 2 3 4 5 6
// inject ShippingProviderFactory into the ShoppingCart Constructor public ShoppingCart(Order order, ShippingProviderFactory shippingProviderFactory) { this.order = order; this.shippingProviderFactory = shippingProviderFactory; }
Also compose the ShippingProviderFactory object on app start
1
var cart = new ShoppingCart(order, new StandardShippingProviderFactory());
Abstract Factory Pattern
The abstract factory pattern provides a way to encapsulete a group of individual factories that have a common theme without specifying their concrete classes.
It adds another layer of abstraction which allow users to choose which factory to use on app start.
Different factories have the same methods but with different implementations
public ISummary CreateSummary(Order order) { return new CSVSummary(); } }
public class SwedenPurchaseProviderFactory : IPurchaseProviderFactory { public IInvoice CreateInvoice(Order order) { if (order.Recipient.Country != order.Sender.Country) { return new NoVATInvoice(); } return new VATInvoice(); }
public ShippingProvider CreateShippingProvider(Order order) { ShippingProviderFactory shippingProviderFactory;
if (order.Sender.Country != order.Recipient.Country) { shippingProviderFactory = new GlobalExpressShippingProviderFactory(); } else { shippingProviderFactory = new StandardShippingProviderFactory(); }
The concrete factory object will be instantiated on app starts(or based on user input).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
IPurchaseProviderFactory purchaseProviderFactory;
if (order.Sender.Country == "Sweden") { purchaseProviderFactory = new SwedenPurchaseProviderFactory(); } else if (order.Sender.Country == "Australia") { purchaseProviderFactory = new AustraliaPurchaseProviderFactory(); } else { throw new Exception("Country not supported."); }
var cart = new ShoppingCart(order, purchaseProviderFactory);
Factory Pattern in Testing
Extract creation of mocked, facked or commonly oused intances in tests.
We could use the Factory Pattern in our Unit Tests. it will be easier to test the parts that use them as you can inhect faked or mocked implementations
public abstract class OrderFactory { protected abstract Order CreateOrder();
public Order GetOrder() { var order = CreateOrder();
order.LineItems.Add( new Item("testA", "testB", 100m), 1 );
order.LineItems.Add( new Item("TestC", "TestD", decimal.MaxValue), 1 );
return order; } }
public class StandardOrderFactory : OrderFactory { protected override Order CreateOrder() { var order = new Order { Recipient = new Address { To = "Yuan", Country = "Australia" }, Sender = new Address { To = "Someone else", Country = "Australia" } };
return order; } }
public class InternationalOrderFactory : OrderFactory { protected override Order CreateOrder() { var order = new Order { Recipient = new Address { To = "Yuan", Country = "Australia" }, Sender = new Address { To = "Someone else", Country = "Sweden" } };
return order; } }
Summary
Separates the client(ShoppingCart) from the creation
Introduce subclasses (StandardShippingProviderFactory, GlobalExpressShippingProviderFactory) and concrete implementations to add functionality.
Factory Pattern is very common when writing tests
Maybe you could buy me a cup of coffee.
Scan this qrcode
Open alipay app scan this qrcode, buy me a coffee!
Scan this qrcode
Open wechat app scan this qrcode, buy me a coffee!