-
Factory pattern is one of the types of creational patterns. You can make out from the name factory itself it’s meant to construct and create something. In software architecture world factory pattern is meant to centralize creation of objects. Below is a code snippet of a client which has different types of invoices. These invoices are created depending on the invoice type specified by the client. There are two issues with the code below:-
-
First we have lots of ‘new’ keyword scattered in the client. In other ways the client is loaded with lot of object creational activities which can make the client logic very complicated.
Second issue is that the client needs to be aware of all types of invoices. So if we are adding one more invoice class type called as ‘InvoiceWithFooter’ we need to reference the new class in the client and recompile the client also.
Figure: - Different types of invoice
Taking these issues as our base we will now look in to how factory pattern can help us solve the same. Below figure ‘Factory Pattern’ shows two concrete classes ‘ClsInvoiceWithHeader’ and ‘ClsInvoiceWithOutHeader’.
The first issue was that these classes
are in direct contact with client which leads to lot of ‘new’ keyword
scattered in the client code. This is removed by introducing a new class
‘ClsFactoryInvoice’ which does all the creation of objects.
The second issue was that the client code is aware
of both the concrete classes i.e. ‘ClsInvoiceWithHeader’ and
‘ClsInvoiceWithOutHeader’. This leads to recompiling of the client code
when we add new invoice types. For instance if we add
‘ClsInvoiceWithFooter’ client code needs to be changed and recompiled
accordingly. To remove this issue we have introduced a common interface
‘IInvoice’. Both the concrete classes ‘ClsInvoiceWithHeader’ and
‘ClsInvoiceWithOutHeader’ inherit and implement the ‘IInvoice’
interface.
The client references only the ‘IInvoice’ interface which results in
zero connection between client and the concrete classes (
‘ClsInvoiceWithHeader’ and ‘ClsInvoiceWithOutHeader’). So now if we add
new concrete invoice class we do not need to change any thing at the
client side.
In one line the creation of objects is taken care by ‘ClsFactoryInvoice’ and the client disconnection from the concrete classes is taken care by ‘IInvoice’ interface.
In one line the creation of objects is taken care by ‘ClsFactoryInvoice’ and the client disconnection from the concrete classes is taken care by ‘IInvoice’ interface.
Figure: - Factory pattern
Below are the code snippets of how actually factory
pattern can be implemented in C#. In order to avoid recompiling the
client we have introduced the invoice interface ‘IInvoice’. Both the
concrete classes ‘ClsInvoiceWithOutHeaders’ and ‘ClsInvoiceWithHeader’
inherit and implement the ‘IInvoice’ interface.
Figure :- Interface and concrete classes
We have also introduced an extra class ‘ClsFactoryInvoice’ with a
function ‘getInvoice()’ which will generate objects of both the invoices
depending on ‘intInvoiceType’ value. In short we have centralized the
logic of object creation in the ‘ClsFactoryInvoice’. The client calls
the ‘getInvoice’ function to generate the invoice classes. One of the
most important points to be noted is that client only refers to
‘IInvoice’ type and the factory class ‘ClsFactoryInvoice’ also gives the
same type of reference. This helps the client to be complete detached
from the concrete classes, so now when we add new classes and invoice
types we do not need to recompile the client.
Figure: - Factory class which generates objects
Another Example:
In this article we are
discussing the Factory Method pattern. I am using the same ADO.NET Provider
Factory class as the example.
Let us see the challenge and evolve to the solution.
Challenge
You are working on a windows application. The application has 2 types of users based on the database license they have: SQL Server or Oracle.
So for each database operation you need to create the right database classes based on an enumeration DatabaseType.
public void InsertRecord()
{
if (AppContext.DatabaseType == DatabaseType.SqlServer)
{
SqlConnection connection = new SqlConnection();
SqlCommand command = new SqlCommand();
Let us see the challenge and evolve to the solution.
Challenge
You are working on a windows application. The application has 2 types of users based on the database license they have: SQL Server or Oracle.
So for each database operation you need to create the right database classes based on an enumeration DatabaseType.
public void InsertRecord()
{
if (AppContext.DatabaseType == DatabaseType.SqlServer)
{
SqlConnection connection = new SqlConnection();
SqlCommand command = new SqlCommand();
command.Connection =
connection;
command.ExecuteNonQuery();
}
else if (AppContext.DatabaseType == DatabaseType.Oracle)
{
OracleConnection connection = new OracleConnection();
OracleCommand command = new OracleCommand();
command.ExecuteNonQuery();
}
else if (AppContext.DatabaseType == DatabaseType.Oracle)
{
OracleConnection connection = new OracleConnection();
OracleCommand command = new OracleCommand();
command.Connection =
connection;
command.ExecuteNonQuery();
}
}
The above code seems to be complex and needs repeating for each database operation spread throughout the application.
How to stop the repeating code and make things better?
Definition
"Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory method lets a class defer instantiation to subclasses."
Implementation
The above problem can be resolved using Factory Method pattern. Here we provide an abstract class for defining the structure and associating all related objects (using Abstract Factory). Then the sub classes will be created deriving from this abstract class. The sub classes decide which classes to be instantiated.
The advantage is more quality and less code. Here we have to define an abstract class named DbProviderFactory. (already ADO.NET contains it)
public abstract class DbProviderFactory
{
public virtual DbCommand CreateCommand();
public virtual DbCommandBuilder CreateCommandBuilder();
public virtual DbConnection CreateConnection();
public virtual DbDataAdapter CreateDataAdapter();
public virtual DbParameter CreateParameter();
}
Then from the above abstract class we can derive concrete classes.
public class SqlClientFactory : DbProviderFactory
command.ExecuteNonQuery();
}
}
The above code seems to be complex and needs repeating for each database operation spread throughout the application.
How to stop the repeating code and make things better?
Definition
"Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory method lets a class defer instantiation to subclasses."
Implementation
The above problem can be resolved using Factory Method pattern. Here we provide an abstract class for defining the structure and associating all related objects (using Abstract Factory). Then the sub classes will be created deriving from this abstract class. The sub classes decide which classes to be instantiated.
The advantage is more quality and less code. Here we have to define an abstract class named DbProviderFactory. (already ADO.NET contains it)
public abstract class DbProviderFactory
{
public virtual DbCommand CreateCommand();
public virtual DbCommandBuilder CreateCommandBuilder();
public virtual DbConnection CreateConnection();
public virtual DbDataAdapter CreateDataAdapter();
public virtual DbParameter CreateParameter();
}
Then from the above abstract class we can derive concrete classes.
public class SqlClientFactory : DbProviderFactory
public override
DbConnection CreateConnection()
{
return new SqlConnection();
}
{
return new SqlConnection();
}
public override
DbCommand CreateCommand()
{
return new SqlCommand();
}
}
You can see from the above code SqlConnection() and SqlCommand() objects are created and supplied for DbConnection and DbCommand types respctively. Here DbConnection and DbCommand are other abstract classes.
Similarly the Oracle implementation follows:
public class OracleClientFactory : DbProviderFactory
{
public override DbConnection CreateConnection()
{
return new OracleConnection();
}
{
return new SqlCommand();
}
}
You can see from the above code SqlConnection() and SqlCommand() objects are created and supplied for DbConnection and DbCommand types respctively. Here DbConnection and DbCommand are other abstract classes.
Similarly the Oracle implementation follows:
public class OracleClientFactory : DbProviderFactory
{
public override DbConnection CreateConnection()
{
return new OracleConnection();
}
public override
DbCommand CreateCommand()
{
return new OracleCommand();
}
}
Replacing the Original Code
Now we are ready to replace our Insert() method code with the Factory Method classes. Here we are using an AppInit() code to create the factory which will be used throughout the application.
public void AppInit()
{
if (AppContext.DatabaseType == DatabaseType.SqlServer)
AppContext.DbProviderFactory = SqlClientFactory.Instance;
{
return new OracleCommand();
}
}
Replacing the Original Code
Now we are ready to replace our Insert() method code with the Factory Method classes. Here we are using an AppInit() code to create the factory which will be used throughout the application.
public void AppInit()
{
if (AppContext.DatabaseType == DatabaseType.SqlServer)
AppContext.DbProviderFactory = SqlClientFactory.Instance;
else if (AppContext.DatabaseType
== DatabaseType.Oracle)
AppContext.DbProviderFactory = OracleClientFactory.Instance;
}
AppContext.DbProviderFactory = OracleClientFactory.Instance;
}
// NewInsertRecord method
private void NewInsertRecord()
{
DbConnection connection = AppContext.DbProviderFactory.CreateConnection();
DbCommand command = AppContext.DbProviderFactory.CreateCommand();
private void NewInsertRecord()
{
DbConnection connection = AppContext.DbProviderFactory.CreateConnection();
DbCommand command = AppContext.DbProviderFactory.CreateCommand();
command.Connection =
connection;
command.ExecuteNonQuery();
}
From the above code you can see that using Factory Method the code is reduced considerably but at a cost of new classes. The pattern provides much flexibility on adding a new database. (if another customer with PostgreSQL arrives)
Abstract and Concrete Derivation
The following image depicts the relation between the abstract and concrete classes we have discussed.
command.ExecuteNonQuery();
}
From the above code you can see that using Factory Method the code is reduced considerably but at a cost of new classes. The pattern provides much flexibility on adding a new database. (if another customer with PostgreSQL arrives)
Abstract and Concrete Derivation
The following image depicts the relation between the abstract and concrete classes we have discussed.
Note
ADO.NET already includes the DbProviderFactory abstract class and concrete classes like SqlClientFactory, OleDbFactory etc. Additionally, using an ORM like Entity Framework automatically takes care of the database switching. The scenario mentioned here is for learning purposes.
Another Example
The essence of the Factory Pattern is to "Define an interface for creating an object, but let the subclasses decide which class to instantiate. The Factory method lets a class defer instantiation to subclasses." Factory methods encapsulate the creation of objects. This can be useful if the creation process is very complex, for example if it depends on settings in configuration files or on user input.
A C# example of the Factory Pattern
// A Simple Interface
public interface IVehicle
{
void Drive(int miles);
}
// The Vehicle Factory
public class VehicleFactory
{
public static IVehicle getVehicle(string Vehicle)
{
switch (Vehicle) {
case "Car":
return new Car();
case "Lorry":
return new Lorry();
default:
throw new ApplicationException(string.Format("Vehicle '{0}' cannot be created", Vehicle));
break;
}
}
}
// A Car Class that Implements the IVehicle Interface
public class Car : IVehicle
{
public void IVehicle.Drive(int miles)
{
// Drive the Car
}
}
// A Lorry Class that Implements the IVehicle Interface
public class Lorry : IVehicle
{
public void IVehicle.Drive(int miles)
{
// Drive the Lorry
}
}
Reference:
No comments:
Post a Comment