In this article we will create an ASP.Net application using Abstract Factory Pattern.
Agenda
- What is Abstract Factory Pattern?
- Do a walkthrough of an ASP.Net Example written using the traditional Factory Pattern and understand the difficulties.
- How does the Abstract Factory Pattern solve the problem?
- What are the components involved in the Abstract Factory Pattern?
- Recreate the Previous code and implement using an abstract design pattern.
- Class diagram for an abstract design pattern.
Previous Articles
- Design Patterns: Introduction
- Learn Design Pattern - Singleton Pattern
- Learn Design Pattern - Factory Method Pattern
What is Abstract Design Pattern?
- An Abstract Factory is the extension of the Factory method pattern and provides an abstraction one level higher than the factory method pattern.
- In short the Abstract Factory creates an object of some other factories which at the end creates an object of concrete classes. Or we can say the Abstract Factory encapsulates individual factories having a single theme.
- Like a Factory method it falls under the creational pattern.
- According to the Gang of Four: It provides an Interface for creating families of related or dependent objects without specifying their concrete classes.
Why and When to extend Factory method to Abstract Factory Pattern?
If you haven't read Factory Method Pattern please go through it first.
Let's consider the same example we used for the Factory method pattern.
There we had a class called CustomerFactory which creates various customers according to input it gets.
Now the Company decides to extend the application and they say, all customers belonging to a specified country (country 1) and who are of Grade1 get a 200 Rs discount and Grade2 customers belonging to another country (country 2) get a discount of 100Rs, and remaining ____.
So the CustomerFactory ends up with a code which looks like this:
public IBaseCustomer GetCustomer(string CustomerType, string Country)
{
switch(CustomerType)
{
case "1":
switch(Country)
{
case "Country1":
{
IBaseCustomer c = new Grade1Customer();
c.DiscountAmount = 200;
return c;
}
default:
{
IBaseCustomer c = new Grade1Customer();
c.DiscountAmount = 0;
return c;
}
}
case "2":
switch(Country)
{
case "Country2":
{
IBaseCustomer c = new Grade2Customer();
c.DiscountAmount = 100;
return c;
}
default:
{
IBaseCustomer c = new Grade1Customer();
c.DiscountAmount = 0;
return c;
}
}
default:
{
IBaseCustomer c = new Grade1Customer();
c.DiscountAmount = 0;
return c;
}
}
}
.
.
.
.
Client Code
CustomerFactory factory=new CustomerFactory();
IBaseCustomer c= factory. GetCustomer("1","Country1");
c.getAmount()//display;
Problem
-
Factory ends up with too many case (or if) conditions, and every new country results in two or more case conditions.
-
One solution is to create 3 different factories, Country1Factory, Country2Factory and OtherFactory. Which means every new country results in recompilation of client code, also the client needs to be aware of each and every factory.
Abstract Factory Pattern as the solution
Here a higher level factory is created which will act as a factory for other factories and those factories will create the final concrete classes as per specifications.
The Components involved in the Abstarct Factory Method Pattern are:
-
Product: IBaseCustomer
-
Concrete Product: Grade1Customer and Grade2Customer
-
BaseFactory: CustomerFactory
-
Concrete Factory: Country1Factory and Country2Factory
Let's modify the previous code.
Output
Step 1
Create a Base Factory or we can ____ Factory of Factories called CustomerFactory; see:
public class CustomerFactory
{
}
Step 2
Create a static method which will create more factories as per requirements; see:
public static CustomerFactory GetCustomerFactory(string Country)
{
switch(Country)
{
case "Country1":
{
return new Country1Factory();
}
case "Country2":
{
return new Country2Factory();
}
default:
{
return new CustomerFactory();//Returning itself for Default
}
}
}
Step 3
Add a virtual method inside the CustomerFactory for creating final concrete classes; see:
public virtual IBaseCustomer GetCustomer(string CustomerType)
{
switch(CustomerType)
{
case "1":
Grade1Customer c1 = new Grade1Customer();
c1.DiscountAmount = 0;
return c1;
case "2":
Grade2Customer c2 = new Grade2Customer();
c2.DiscountAmount = 0;
return c2;
default:
return null;
}
}
Step 4
Define concrete factories, and inherit them from base factories i.e, from CustomerFactory; see:
CustomerFactory
public class Country1Factory : CustomerFactory
{
}
public class Country2Factory : CustomerFactory
{
}
Step 5
Override virtual methods in concrete factories according to requirements and return final concrete classes; see:
public override IBaseCustomer GetCustomer(string CustomerType)
{
switch(CustomerType)
{
case "1":
Grade1Customer c1 = new Grade1Customer();
c1.DiscountAmount = 200;
return c1;
case "2":
Grade2Customer c2 = new Grade2Customer();
c2.DiscountAmount = 0;
return c2;
default:
return null;
}
}
.
.
.
Step 6
Write Client Code:
CustomerFactory objFactory = CustomerFactory.GetCustomerFactory(DdlCountry.SelectedValue);
IBaseCustomer objCustomer = objFactory.GetCustomer(DdlGrade.SelectedValue);
objCustomer.CustomerName = TxtName.Text;
objCustomer.Amount = int.Parse(TxtAmount.Text);
Response.Write("Welcome " + TxtName.Text + ", Your toal unpaid amount is " + objCustomer.GetTotalAmount().ToString());
Class Diagram
We are done with the third creation pattern, please download the attached zip file for the complete source code.
Hope you enjoyed. Keep reading and comments are always welcome.