Factory Method Design Pattern
Contents
Where to Use It?
It is tedious when the client needs to specify the class name while creating the objects. So, to resolve this problem, we can use Factory Method design pattern. It provides the client a simple way to create the object.
UML
The classes and objects participating in the above UML class diagram are as follows:
Product
This defines the interface of objects the factory method creates
ConcreteProduct
This is a class which implements the Product interface.
Creator
This is an abstract class and declares the factory method, which returns an object of type Product. This may also define a default implementation of the factory method that returns a default ConcreteProduct object.
This may call the factory method to create a Product object.
ConcreteCreator
This is a class which implements the Creator class and overrides the factory method to return an instance of a ConcreteProduct.
Real world example
Assume you have three different cards which are considered here as classes MoneyBack, Titanium and Platinum, all of them implement abstract class CreditCard. You need to instantiate one of these classes, but you don't know which of them, it depends on the user. This is perfect scenario for the Factory Method design pattern.
Who is what?
The classes and objects participating in the above class diagram can be identified as follows:
- Product - CreditCard
- ConcreteProduct- MoneyBackCreditCard, TitaniumCreditCard, PlatinumCreditCard
- Creator- CardFactory
- ConcreteCreator- MoneyBackCardFactory, TitaniumCardFactory, PlatinumCardFactory
Here are the code blocks for each participant
Product
namespace FactoryMethodDesignPatternInCSharp
{
/// <summary>
/// The 'Product' Abstract Class
/// </summary>
abstract class CreditCard
{
public abstract string CardType { get; }
public abstract int CreditLimit { get; set; }
public abstract int AnnualCharge { get; set; }
}
}
ConcreteProduct
MoneyBackCreditCard
using System;
namespace FactoryMethodDesignPatternInCSharp
{
/// <summary>
/// A 'ConcreteProduct' class
/// </summary>
class MoneyBackCreditCard : CreditCard
{
private readonly string _cardType;
private int _creditLimit;
private int _annualCharge;
public MoneyBackCreditCard(int creditLimit, int annualCharge)
{
_cardType = "MoneyBack";
_creditLimit = creditLimit;
_annualCharge = annualCharge;
}
public override string CardType
{
get { return _cardType; }
}
public override int CreditLimit
{
get { return _creditLimit; }
set { _creditLimit = value; }
}
public override int AnnualCharge
{
get { return _annualCharge; }
set { _annualCharge = value; }
}
}
}
TitaniumCreditCard
using System;
namespace FactoryMethodDesignPatternInCSharp
{
/// <summary>
/// A 'ConcreteProduct' class
/// </summary>
class TitaniumCreditCard : CreditCard
{
private readonly string _cardType;
private int _creditLimit;
private int _annualCharge;
public TitaniumCreditCard(int creditLimit, int annualCharge)
{
_cardType = "Titanium";
_creditLimit = creditLimit;
_annualCharge = annualCharge;
}
public override string CardType
{
get { return _cardType; }
}
public override int CreditLimit
{
get { return _creditLimit; }
set { _creditLimit = value; }
}
public override int AnnualCharge
{
get { return _annualCharge; }
set { _annualCharge = value; }
}
}
}
PlatinumCreditCard
using System;
namespace FactoryMethodDesignPatternInCSharp
{
/// <summary>
/// A 'ConcreteProduct' class
/// </summary>
class PlatinumCreditCard : CreditCard
{
private readonly string _cardType;
private int _creditLimit;
private int _annualCharge;
public PlatinumCreditCard(int creditLimit, int annualCharge)
{
_cardType = "Platinum";
_creditLimit = creditLimit;
_annualCharge = annualCharge;
}
public override string CardType
{
get { return _cardType; }
}
public override int CreditLimit
{
get { return _creditLimit; }
set { _creditLimit = value; }
}
public override int AnnualCharge
{
get { return _annualCharge; }
set { _annualCharge = value; }
}
}
}
Creator
namespace FactoryMethodDesignPatternInCSharp
{
/// <summary>
/// The 'Creator' Abstract Class
/// </summary>
abstract class CardFactory
{
public abstract CreditCard GetCreditCard();
}
}
ConcreteCreator
MoneyBackFactory
namespace FactoryMethodDesignPatternInCSharp
{
/// <summary>
/// A 'ConcreteCreator' class
/// </summary>
class MoneyBackFactory : CardFactory
{
private int _creditLimit;
private int _annualCharge;
public MoneyBackFactory(int creditLimit, int annualCharge)
{
_creditLimit = creditLimit;
_annualCharge = annualCharge;
}
public override CreditCard GetCreditCard()
{
return new MoneyBackCreditCard(_creditLimit, _annualCharge);
}
}
}
TitaniumFactory
namespace FactoryMethodDesignPatternInCSharp
{
class TitaniumFactory: CardFactory
{
private int _creditLimit;
private int _annualCharge;
public TitaniumFactory(int creditLimit, int annualCharge)
{
_creditLimit = creditLimit;
_annualCharge = annualCharge;
}
public override CreditCard GetCreditCard()
{
return new TitaniumCreditCard(_creditLimit, _annualCharge);
}
}
}
PlatinumFactory
namespace FactoryMethodDesignPatternInCSharp
{
class PlatinumFactory: CardFactory
{
private int _creditLimit;
private int _annualCharge;
public PlatinumFactory(int creditLimit, int annualCharge)
{
_creditLimit = creditLimit;
_annualCharge = annualCharge;
}
public override CreditCard GetCreditCard()
{
return new PlatinumCreditCard(_creditLimit, _annualCharge);
}
}
}
Factory Pattern Client Demo
Code
using System;
namespace FactoryMethodDesignPatternInCSharp
{
/// <summary>
/// Factory Pattern Demo
/// </summary>
public class ClientApplication
{
static void Main()
{
CardFactory factory = null;
Console.Write("Enter the card type you would like to visit: ");
string car = Console.ReadLine();
switch (car.ToLower())
{
case "moneyback":
factory = new MoneyBackFactory(50000, 0);
break;
case "titanium":
factory = new TitaniumFactory(100000, 500);
break;
case "platinum":
factory = new PlatinumFactory(500000, 1000);
break;
default:
break;
}
CreditCard creditCard = factory.GetCreditCard();
Console.WriteLine("\nYour card details are below : \n");
Console.WriteLine("Card Type: {0}\nCredit Limit: {1}\nAnnual Charge: {2}",
creditCard.CardType, creditCard.CreditLimit, creditCard.AnnualCharge);
Console.ReadKey();
}
}
}
Output
Here, we are accepting input from the clients/users as to which class they want to instantiate.
Enter the respective card type: (MoneyBack/Titanium/Platinum)
How does Factory Method promote loosely coupled code?
- It provides extension points to add new behaviour
- It's similar to Template Method (https://sourcemaking.com/design_patterns/template_method)
- It complies with the following SOLID principles:
- S: Single responsibility in that object creation is deferred to another object, in this case the ConcreteCreator. And a single reason to change in that the algorithm invoking the factory method/s shouldn't need to change to support new behaviour
- O: Open/closed, similar to the above in that the method invoking the factory method/s is open for extension by subtypes providing specialised objects that can provide different behaviour
- L: Liskov compliant in that subtypes providing specialisation via factory methods can be used in place of their parent types
- D: Dependency inversion, the creation of objects is delegated to factory methods and the method invoking the factory methods depends on abstractions
Here is the link to the repo we were looking at with the MazeGame and BombedMazeGame classes: https://github.com/Squaretechre/gof-design-patterns
See Also : [https://www.c-sharpcorner.com/article/factory-method-design-pattern-in-c-sharp/



