Design patterns in ASP.NET 3.5
From ThemesWiki
| Official Page |
| Project Documentation |
| Download |
|
This tutorial is devoted exclusively to design patterns and how we can use some of the famous design patterns in our ASP.NET web applications.
In this tutorial, we will focus on the following:
- Understanding different categories of design patterns
- Implementing the Singleton pattern
- Implementing the Factory pattern
- Understanding and implementing Dependency Injection
- Understanding the Command Design Pattern
Contents |
[edit] Understanding Design Patterns
Design patterns not only help us to solve problems, but also gives us an assurance that the solutions will work and are stable, because these patterns have been tried and tested over the years by many other programmers. As we read earlier, design patterns are simple, and provide the best solutions to some of the common problems that developers face. Hence, they are more reliable than an ingenious solution to a problem that may have already been solved by others.
Any software problem can have many solutions, as depicted by the following diagram:
Some of these solutions might be innovatively new and use creative methods of finding an approach and a possible answer to a problem. Some solutions might be workable but not efficient. When it comes to deciding for the best approach for our current problem, we should first try to learn from the past, and understand how similar problems might have been solved by others. This is where design patterns come into picture.
[edit] History of Patterns
Much of the foundation of the design patterns was laid down by the "Gang of Four" (GoF) Erich Gamma, Richard Helm, Ralph Johnson and John Vlissides, in their famous tutorial "Design Patterns". The Gang of Four categorized design patterns into three groups:
- Creational
- Structural
- Behavioral
Creational patterns deal with different ways of creating an object to suit a particular scenario. One of the famous examples is the factory design pattern.
Structural patterns are related to the way in which a group of objects should be structured to achieve a goal, and how the relationships should be maintained between different objects. The Adapter pattern is a well known example of structural design pattern.
Behavioral patterns are related to how the objects co-ordinate and communicate with each other. The Command design pattern is a famous behavioral pattern.
An important point to note is that sometimes a design pattern may use some other design patterns to solve a particular problem. So a design pattern is not atomic in nature but may comprise of multiple patterns. Also, there is no strict rule when following a design pattern, which means you don't need to copy it word-for-word, or code-by-code. A design pattern is just an approach that has worked well in similar cases for other developers. Not all situations or programming problems are the same. Hence, if we are following a particular design pattern we might need to modify it to suit our custom needs, instead of blindly following it verbatim.
There are a lot of design patterns, and discussing all of them is beyond the scope of this tutorial. So we will focus only on a few important, basic ones that every developer must be aware of when developing web applications in ASP.NET.
[edit] Singleton Pattern
Singleton is a creational design pattern that helps us restrict and control the number of objects instantiated for a particular class throughout the application life cycle. In this section, we will understand this design pattern by examining some example code. Singleton is one of the most widely used patterns in ASP.NET.
A common programming problem is to create a single instance of an object and make sure that there are no other instances except this single instance. Only this instance should serve all incoming requests and communicate with other objects. The Singleton pattern is a design pattern that can be used to implement such scenarios.
There may be numerous programming scenarios when we may need to restrict an object to a single instance. Some of them are:
- A single instance of a mail server might be required to process all incoming mail requests.
- The
Sessionobject in ASP.NET is implemented using singleton pattern. That is why each user will have only one session instance accessible at any point of time. - The
Applicationobject in ASP.NET is also singleton based. There is only one instance of theApplicationobject for an entire application. - We may need a single instance of a logging utility to process all logging requests in our application.
The ASP.NET framework itself implements a singleton pattern. Besides, in the Session object, the singleton pattern can be seen in the way a framework handles the worker process. We have one and only one instance of the work process catering to all incoming HTTP requests.
[edit] Understanding Singleton with Code Example
Let's understand the Singleton pattern with the help of an example. In our OMS application, we have many orders coming in, and we want all customers to be notified whenever they place an order. For this, we have an EmailManager class. This class has the responsibility to send emails to all customers who have placed orders. Now, because ASP.NET is multi-threaded (it creates a new thread for each new client request), if we start creating a new instance of the EmailManager (as in EmailManager em = new EmailManager()) to process emails, we will have a lot of such instances, for multiple requests. In simple terms, if 50 customers placed their orders through the web site, we will have 50 instances of the EmailManager object. So let's assume that the requirement is to handle all emails in one instance and avoid having many instances as this might have a performance impact on server memory.
|
Ideally, we should have a separate email application which would be handling all of these application-related emails. There can be different ways of handling emails in different applications. However, for the purpose of understanding design patterns, we will assume that we have to use Singleton to restrict the creation of |
So how can we make sure that there is one and only once instance of the EmailManager class throughout the life of our application? One approach is the use of Static classes in ASP.NET. Static classes are defined using the static keyword and the .NET compiler makes sure that there is no instance of a static class, and its methods can be called without creating any instance, as in:
public static EmailManager
{
public static MyMethod()
//other static methods
}
Now, the problem with this approach is that we cannot use static classes as method arguments and this might severely limit our scope. We may need to pass an instance of the EmailManager class to some other method, but with static classes we cannot do so.
Also, if we want to have different implementations of EmailManager created with different functionality, but with the same behavior (by using interfaces and then having different implementational classes), we will not be able to do that with static classes. Anyways, the singleton pattern is about ensuring that only a single "instance" of our class is always created, and to achieve this goal making our classes static is neither a good approach nor is it feasible in many cases. So let us look at a better approach.
The next strategy would be:
To use the singleton design pattern. The following code shows our class (this code is a stripped-down and simplified version; to have working code it is recommended that you follow the code provided in the code bundle):
public sealed class EmailManager
{
private static EmailManager _manager;
//Private constructor so that objects cannot be created
private EmailManager()
{
}
public static EmailManager GetInstance()
{
// Use 'Lazy initialization'
if (_manager == null)
{
//ensure thread safety using locks
lock(typeof(EmailManager)
{
_manager = new EmailManager();
}
}
return _manager;
}
}
Now let us understand the code step-by-step:
1. public sealed class EmailManager: We have used the sealed keyword to make our EmailManager class uninheritable. This is not necessary, but there is no use having derived classes as there can be only one instance of this class in memory. Having derived class objects will let us create two or more instances which will be against the singleton's design objective.
2. private static EmailManager _manager: Next, we create a variable named _manager, which holds a reference to the single instance of our EmailManager class. We have used a static modifier because we will be accessing this variable from a static method GetEmailManager(), and static methods can use only static variables.
private EmailManager(){
}
We need to create a private constructor to make sure that we don't accidentally initialize an object of the GetEmailManager class. By only initializing via the static GetInstance() method, we should get a single instance of this class.
3. public static EmailManager GetInstance()
{
// Use 'Lazy initialization'
if (_manager == null)
{
//ensure thread safety using locks
lock(typeof(EmailManager)
{
_manager = new EmailManager();
}
}
return _manager;
}
GetInstance() is the static method that we will use from outside this code to get the current reference of the EmailManager class. In this method, we are using the lazy loading technique (discussed in Tutorial 4) to load the instance on demand. We first check if the current instance is null or not. If it is null, then we create a new one; otherwise we return the existing static instance.
lock(typeof(EmailManager)
4.The private _manager object, marked static, is used inside a critical section (using lock) to make sure it is thread safe. Thread safety is very important here. Otherwise, two threads might simultaneously call GetInstance() and, on finding the EmailManager instance (_manager) null, will both try to create an instance, thereby creating two instances of the class. The lock keyword helps us make sure that once a thread enters the region, no other thread can do so until the first thread exits, making our code thread safe. We pass the EmailManager's type in order to lock the statement using the typeof operator to define the scope of the lock statement.
An important point to note is that in the above code we have to make sure that the type used in the typeof() command is not publicly accessible, otherwise the scope would be affected. It is better to create a private object within our class to use as a reference object in the lock statement, as in:
private static object forLock = new object();
public static EmailManager GetInstance()
{
// Use 'Lazy initialization'
if (_manager == null)
{
//ensure thread safety using locks
lock(typeof(forLock)
{
_manager = new EmailManager();
}
}
return _manager;
}
So the above code can be used for implementing a Singleton design pattern in ASP.NET effectively and safely. Now we will move to another famous design pattern the Factory method.
[edit] Factory Method
The Factory design pattern is another heavily-used pattern in ASP.NET applications to help introduce loose coupling and remove dependencies in the code. The Factory method is a creational design pattern that is used to create objects without any prior knowledge of the type of the object. We delegate the responsibilities of creating the actual objects to subclasses or separate factory classes. This can be accomplished by using interfaces or abstract classes.
Why do we need this design pattern? Change is one thing that we cannot avoid in software development. No matter how strictly we jot down specifications of the software we are making, there will always be some changes in the future. And each such change might affect the application code base. So there is no way of avoiding changes, and unless our applications are designed to adapt to these changes, we will be spending more time and money on changing the application code each time a change is requested. The factory design helps us make our applications "change-friendly".
Here is a representative class diagram showing how a Factory pattern might be used in an object model:
We have IConcreteObject, an interface to be implemented by all of the ConcreteObject classes. We use this interface to abstract all methods.
We have three concrete object classes implementing the IConcreteObject, ConcreteObject1, ConcreteObject2, and ConcreteObject3 interfaces.
Now, we have an interface named Ifactory and the ConcreteFactory class is implementing that interface and is responsible for creating the concrete objects (ConcreteObject1, ConcreteObject2, or ConcreteObject3). It is up to the concrete factory as to which class is to be instantiated.
So the responsibility for creating the object is delegated to the ConcreteFactory class. Let us first understand the basic Factory design , through the use of a simple practical example.
[edit] Core Principle: Programming to an Interface instead of an Implementation
One of the core principles of object-oriented programming is to always program to an interface instead of a concrete object. Let us understand this in detail. Assume that we have multiple kinds of products in our OMS (Order Management System) such as foods, electronics and beauty products. To encapsulate these different products, we create different classes, such as FoodProducts, ElectronicProducts, BeautyProducts, and so on.
Because each product will have common properties and methods shared by all kinds of products, for example Unit Cost, Size, Weight, and so on, we create an interface called IProducts to list the basic behavior of a product.
public interface IProduct
{
float UnitCost { get; set;}
float Weight {get; set;}
bool Update();
}
In this interface-IProduct- we have defined two basic fields with both getter and setter properties, along with a method. (There can be more, but for the purpose of our understanding, we will work with only these two properties and one method.)
Next, we create individual concrete product classes (one for each different product). Here is one such class, BeautyProduct:
public class BeautyProduct:IProduct
{
private float _unitCost;
private float _weight;
private int _forGender;
public float UnitCost
{
get { return _unitCost; }
set { _unitcost = value; }
}
public float Weight
{
get { return _ weight; }
set { _ weight = value; }
}
public int ForGender
{
get { return _ forGender; }
set { _ forGender = value; }
}
public bool Update()
{
try
{
//code to update the product calling DAL method return ProductDAL.Update(this);
}
catch(Exception ex)
{
//log and rethrow
}
}
In the above concrete class, we have implemented the IProduct interface with concrete methods and properties. Note that we have added a property called ForGender, which tells us the gender group to which this beauty product belongs. This can be male, female or unisex, with each value being represented by an integer, as in 1, 2, or 3.
Because this is a beauty product specific property, it is not present in the interface, IProduct.
Now if we want to use BeautyProduct objects in our code, the first and simple approach would be:
BeautyProduct bp = new BeautyProduct();
Let us understand this line in more detail. The new keyword is used to create a new instance of a class. When using the new keyword, there are two important points to consider the "type" of the object on the left-side and the right-side of the new keyword. In the above case, we are programming to a concrete implementation, that is, BeautyProduct, so the type of the object bp is BeautyProduct, and the implementation type is also BeautyProduct (as we have used in this syntax, new BeautyProduct()).
The problem with this method of using an object is that there is no flexibility, and the objects are tightly coupled with their class types. For example, we might need to use the Product object in different cases, as we might have a ProductManager class that will create and return a product to be added in an OrderLineItem object (this class will return an appropriate product object based on the type of product a customer has ordered). The following is a sample code of such a ProductManager class that returns a BeautyProduct object:
public class ProductManager
{
//misc. methods to handle products
//
public BeautyProduct OrderProduct()
{
BeautyProduct bp = new BeautyProduct();
//set bp properties
bp.OrderDate = Datetime.Now;
//reset the product count in the inventory as this product has been //ordered bp.ResetInventory();
return bp;
}
}
In the above code sample, we have the OrderProduct() method that creates a new instance of BeautyProduct, and returns it to the consuming class (which might use this object to perform operations on it, as in an Order class).
|
We will not be focusing on other methods in the |
The problem with this way of programming is that it makes our application too rigid, because to create another type of product object, say ElectronicProduct, we need to write another OrderProduct method in the ProductManager class. The ProductManager class returns an object of type, ElectronicProduct, which looks something like this:
public ElectronicProduct OrderEletronicProduct() {
ElectronicProduct ep = new ElectronicProduct ();
//set ep properties
ep.OrderDate = Datetime.Now;
//reset the product count in the inventory as this product has been //ordered ep.ResetInventory();
return ep;
}
So for each type of product, we need to add multiple order methods with different return signatures, which is quite messy. Also, in future, if we add a new type of product, we need to open our ProductManager class and modify it to make sure it can create and return the new product type. So this approach makes our code very rigid, inflexible and open to modifications each time there is a change. To avoid this, we can use Polymorphism and program to interfaces instead of using the concrete classes.
So what does 'programming to interfaces' mean? We create an object using the interface/super class as the type instead of the concrete classes. This means:
BeautyProduct bp = new BeautyProduct();
becomes
IProduct bp = new BeautyProduct();
Note that the type of the bp object is now the interface, making it possible for us to switch to different concrete types during code execution. This is known as Runtime polymorphism. Let us see how we can use this approach to make our ProductManager class better:
public class ProductManager
{
public IProduct OrderProduct()
{
//misc. methods
IProduct bp = new BeautyProduct();
//set bp properties
bp.OrderDate = Datetime.Now;
//reset the product count in the inventory as this product has been //ordered
bp.ResetInventory();
return bp;
}
}
Now, because we have used IProduct in the method signature, we can pass in all current and future concrete implementation classes in this method as long as they implement the IProduct interface:
IProduct p = new EletronicProduct(); return p;
Also, programming to interfaces gives us the flexibility of Dynamic polymorphism. We don't have to worry about the type of concrete implementations, as they are all deriving from a common abstract class or implementing a common interface. There is something we avoid writing multiple Create methods for different product types.
[edit] The Need for Factory Design
So far so good. But we still need to make sure that our OrderProduct method can return multiple product types. How does the code in OrderProduct() method know which concrete type to return? One option is that we modify the code so that it can include the type of the product passed as a second parameter. Then, by using a switch case or if else statements, we can have different creational logic implemented for each concrete type, as follows:
public IProduct OrderProduct(string productType) {
IProduct product;
switch(productType)
{
case "BeautyProduct": product = new BeautyProduct(); break;
case "ElectronicProduct": product = new ElectronicProduct (); break;
case "FoodProduct": product = new FoodProduct (); break; default: break;
}
//set product properties
product.OrderDate = Datetime.Now;
//reset the product count in the inventory as this product has been //ordered product.ResetInventory();
return product;
}
As you can see, we now face another issue. If we add a new product in future, we will still need to modify our OrderProduct method to add another switch case statement for the newly-added product, so that OrderProduct can return it.
To fix this issue, and to make our code more elegant and flexible, we need to abstract the product creation logic to some other class. This is where the factory design can help us.
We take the product creation code out of the ProductManager class and create a new class to handle and return the correct type of product object. So the only role of this new class would be to create and return product objects based on the object type passed to it. This will make our ProductManager class independent of handling the correct product type and allow it to focus on other important business rules that may need to be applied for managing products.
We will call this class ProductFactory. Here is the code for that class:
public class ProductFactory
{
public IProduct CreateProduct(string productType) {
IProduct product = null;
switch(productType)
{
case "BeautyProduct": product = new BeautyProduct(); break;
case "ElectronicProduct": product = new ElectronicProduct (); break;
case "FoodProduct": product = new FoodProduct (); break;
default: break;
}
return product;
}
}
This factory class takes the product type as a string parameter and simply returns the appropriate concrete product object based on this parameter. Here is how our ProductManager will use this factory class:
public class ProductManager
{
ProductFactory factory= new ProductFactory();
IProduct product;
//misc. methods to handle products
public IProduct OrderProduct(string productType)
{
product = factory.CreateProduct(productType);
//set product properties
product.OrderDate = Datetime.Now;
//reset the product count in the inventory as this product has been //ordered product.ResetInventory();
return product;
}
}
In the above code, the ProductFactory class has abstracted the product creation logic. So if there are new products to be added in future, we don't need to change the code in the ProductManager class; only the factory class needs to be changed. We have de-coupled the ProductManager class and made it more flexible by adding another level of indirection in terms of a factory class.
Another example where you will find factory design useful is when you want your application to be able to talk to different database types. You may not know which database each of your clients (to whom you sold your application) has. It could be MS SQL Server, Oracle, FireBird, PostGres, or MySql.
You have developed the data access layer classes for each of these databases in your application code. Now you want your application to be able to instantiate any of the DAL classes based on the actual customer database. Our application does not know beforehand which database will be used. For such cases, and similar scenarios, the factory design pattern can help.
We will see another slightly different, real world, practical example of a Factory design in the next design pattern, Dependency Injection (DI).
[edit] Dependency Injection
The Dependency Injection and factory design patterns are very common, and provide great flexibility in software development. Although most programmers have come across these patterns, they may not grasp the concepts completely until they see these patterns in action in real projects.
In this section, we will learn how to achieve loose coupling and "plug-and-play" architecture using these patterns, with the help of a sample project a flexible encryption program. We will be focusing on the code from the viewpoint of understanding the Dependency Injection design pattern. Therefore, the detailed syntax and complete code will not be listed here. A complete working code for this example is provided in the code bundle.
The Dependency Injection (DI) design pattern is "a form of" the Inversion of Control (IoC) design which is applied in many frameworks. DI gives the flexibility of attaching a custom implementation such as a "plugin", without modifying existing software. Dependency Injection can be achieved using Constructor, Setter, or Interface Injection. In this tutorial, we will learn and understand Interface Injection, which is quite common and more flexible than the other two approaches.
[edit] Basic Approach
We will follow a set of steps to achieve Dependency Injection in our working sample, as described below.
[edit] Step 1: Create an Interface
Let us start with our encryption program by authoring an interface so that others can implement their own algorithmic implementations by defining these methods in their own custom way:
public interface IEncryptionAlgorithm
{
string Password
{
get;
set;
}
byte[] RawInput
{
get;
set;
}
byte[] Salt {
set;
}
int KeySize
{
set;
}
byte[] Encrypt();
byte[] Decrypt();
bool CheckPassword();
}
In the above code, we have created an interface named IEncryptionAlgorithm, which will abstract the basic properties that every concrete algorithm needs to implement. This interface is a kind of a contract which the implementation classes need to follow for writing custom encryption methods. We have:
-
Password: the password/key for each encryption algorithm -
RawInput: a byte array which will hold the data that needs to be encrypted -
Salt: salt is needed to make sure that our encryption code is harder to crack -
Keysize: the bigger the key size, the tougher it is to break the encryption -
Encrypt()method: returns the data as a byte array after encrypting it -
Decrypt()method: decrypts already-encrypted data back to the original data -
CheckPassword():for demonstration purposes, we will be storing the password inside the encrypted data instead of some external location. So this method will be used before the actual decryption to check if the password entered by the user is correct or not.
[edit] Step 2: Create an Implementation
We first created an XOR based XOREncryption class, which implemented this interface:
public class XOREncryption : IEncryptionAlgorithm
We will implement the encryption and decryption methods along with the properties; for detailed code refer the code bundle. Here is the trimmed -0 down version:
public byte[] Encrypt()
{
byte[] encryptedBytes = new byte[_rawInput.Length];
byte[] keyBytes = ASCIIEncoding.ASCII.GetBytes(_key);
//hard coded salt value
_salt = new byte[] {0x11, 0x78, 0x22, 0xFF, 0xAC, 0x5C,
0x78, 0x4E, 0x7D, 0x45, 0xEF, 0xF1};
//rest of the code goes here
}
We need to complete this class with other methods and properties defined by the interface (see the source code provided in the code bundle).
[edit] Step 3: Create another Implementation
Because we want to switch between multiple algorithmic implementations at runtime, we will create one more encryption algorithm implementation so that we can see the plug-n-play design in action:
public class RijndaelEncryption : IEncryptionAlgorithm
{
//implement IEncryptionAlgorithm properties and methods
}
Now we have two implementations ready, and the question becomes how we dynamically instantiate one of these in the GUI.
[edit] Step 4: Create a Factory Class
For that, we need to use the Factory design pattern and create a Factory class as follows:
public sealed class AlgorithmFactory
{
private AlgorithmFactory()
{ }
public static IEncryptionAlgorithm GetSpecifiedAlgorithm()
{
string algoType = System.Configuration. ConfigurationSettings.AppSettings["algo"];
IEncryptionAlgorithm algoInstance;
if (string.IsNullOrEmpty(algoType))
{
goInstance = Activator.CreateInstance(Type. GetType("NeekProtect.XOREncryption,NeekProtect"))
as IEncryptionAlgorithm;
}
else
{
algoInstance = Activator.CreateInstance(Type. GetType(algoType)) as IEncryptionAlgorithm;
}
return algoInstance;
}
}
This Factory class is responsible for creating and returning concrete implementations based on the algorithm of our choice (specified in the web.config file). This class is sealed, because it doesn't make sense to inherit it. Also, the constructor is private, so its instance cannot be created at all, and all methods are made static. Why static? Because AlgorithmFactory is behaving like a Helper class, helping the clients to get an instance of the appropriate Algorithm implementation class. See the static GetSpecifiedAlgorithm() method; it returns an instance of IEncryptionAlgorithm. This is where we see the important design principle "Always program to interfaces". Because each encryption algorithm class implements IEncryptionAlgorithm, the supertype of each implementation is the same IEncryptionAlgorithm. Hence, we don't need to worry about the actual implementation class as long as the class implements the same interface.
[edit] Step 5: Implement the Configuration Settings
Here is how we get the type specified in the application config file:
string algoType = System.Configuration.ConfigurationSettings.AppSettings["algo"];
In the config file, the following entry is made (under appSettings):
<add key="algo" value="NeekProtect.XOREncryption,NeekProtect"/>
In the value, we specify the fully qualified class name and the assembly name in which the class is present, separated by a comma. We need this entry as we will dynamically load the assembly using Activator.CreateInstance, as:
IEncryptionAlgorithm algoInstance; algoInstance = Activator.CreateInstance(Type.GetType(algoTyp)) as IEncryptionAlgorithm;
Let us now see the client, and how it uses this instance. The class EncryptionEngine is our client here:
/// <summary>
/// This class prepares the byte array and sets files and directories /// to be encrypted and calls the relevant encryption/decryption /// algorithm
/// </summary>
public class EncryptionEngine
{ .}
This class has a method EncryptFile, as follows:
private void EncryptFile(string fullPath, string password)
{
IEncryptionAlgorithm x = AlgorithmFactory.GetSpecifiedAlgorithm();
x.Password = password;
x.KeySize = 128;
string ext = Path.GetExtension(fullPath);
byte[] extByte = Encoding.ASCII.GetBytes(ext);
byte[] inputByte = File.ReadAllBytes(fullPath);
MemoryStream m = new MemoryStream();
m.Write(inputByte, 0, inputByte.Length);
m.Write(extByte, 0, extByte.Length);
x.RawInput = m.ToArray();
byte[] o = x.Encrypt();
..//other stuff
}
We call the AlgorithmFactory's static method GetSpecifiedAlgorithm(), which returns an instance of the class specified in the config file. Here, we don't care how the instance is returned; we just need an instance of IEncryptionAlgorithm so that we can encrypt the input file. This is another example of programming to interfaces.
[edit] Step 6: Implement another Custom Algorithm
In the above code, there is no mention of the two encryption classes we have coded earlier: XOREncryption and RijndaelEncryption. We only use IEncryptionAlgorithm in this code, and this gives us the flexibility to use any custom algorithm as long as it implements IEncryptionAlgorithm.
Let's suppose a user installs our encryption program, and is not satisfied by the two default algorithms provided. Now he or she wishes to use the 3DES algorithm with the program. How can he or she do that?
The user will need to create their own 3DESEncryption class and implement all of the methods and properties specified in the IEncryptionAlgorithm interface, as follows:
public class 3DESEncryption: IEncryptionAlgorithm
{
//implement IEncryptionAlgorithm properties and methods
}
Now the user has to compile it and place the resultant assembly in the folder where our program is installed, which is probably something like this: C:\Program Files\PP\Encryption Program.
In the same folder, the user has to open and edit the XML configuration file (usually this file is named as App.GUI.exe.config) and create/overwrite the value of the key algo as:
<add key="algo" value="MyCustomAlgo.3DESEncryption, MyCustomAlgo "/>
(For the sake of illustration, we have assumed the namespace to be MyCustomAlgo).
Now the Factory class will return this instance to be used in our encryption program. So we see how the "plugin" type functionality can be achieved by using the Dependency Injection, and users can build their own custom implementations and plug them into our program.
[edit] Command Design Pattern
The Command design pattern is used to decouple the calling object and the object that is being called, so that the calling object does not know how its request will be executed by the called object. Let us understand this with the help of an example.
Usually, in association or aggregation relationships, the object holding a reference to another object will communicate with this object by invoking a method on it. For example, in our OMS web application, the AddEditCustomer.aspx.cs page object has a reference to the Customer object (an association, as the Customer object is used locally). The page object loads a particular Customer object by calling a method on it, for example:
Customer customer = new Customer(); customer.Load(customerID);
So the page object is interacting with the customer object by issuing a command to it (Load(), in our case). This is how objects usually interact and communicate with each other by invoking commands.
[edit] Decoupling the GUI completely from the BL
Now let's take another scenario. In Tutorial 4, we saw how we can use a 5-tier configuration and introduce loose coupling into our projects. In the sample OMS code, we saw that the GUI and the BL were loosely coupled, but the GUI was dependent on the BL in the sense that we had a reference to the BL in our GUI code (using statements referring to the BL Tier). What if we want the BL and the GUI to be completely independent of each other, where there is not even a one way reference between the GUI and the BL. This means that the GUI will not call business layer objects directly, as it doesn't know anything about them. One way to make this happen is to add another level of indirection by
adding another tier. We can call it the Service Layer. The GUI can refer to and call this new Service layer method, which sits in between the GUI and the Business Layer and handles the abstraction. Here, the GUI talks to the Service Layer, and the Service Layer in turn interacts with the Business Layer.
This decoupling will make our application more flexible as the GUI has no reference to the BL tier, and it does not need to know how the business logic will be handled. We can have multiple GUIs for the same business logic, and multiple business logic implementations for the same GUI. This complete independence makes software development much more flexible and robust, but also increases complexity.
In this scenario, assume that we are creating a GUI form to add a new customer. In the Add buttons click event handler, we cannot simply create a new Customer object because the GUI has no reference of the Customer class in the Business Layer. Additionally, it does not know which object would be there, and what methods that object would support. In such circumstances, we use the Command design pattern to abstract the invocation of the commands to associated objects.
[edit] Creating the Command Interface
In this design pattern, we create a Command object, which will encapsulate the actual command. Then the GUI will invoke this command object, which will issue the actual command. So a GUI object may be referred to as an "invoker" here, and the service layer would be the "receiver". First, we define an interface ICommand, and every concrete command implementation will implement this interface. This command infrastructure will help us decouple the GUI and the BL.
The ICommand interface will have only one method, Execute(), as follows:
public interface ICommand
{
CommandResult Execute(CommandArg cmdArg);
}
[edit] Creating the Value Objects
Now we will create the different Command classes, which will implement this interface and actually be responsible for executing commands. Each command might need some arguments, and might return results as well. Because we will not be using custom objects here, we need to create generic wrappers to hold the arguments and the results. We will use the CommandArg class to hold the arguments, and the CommandResults class to hold the results.
Here is the code for the CommandArg class:
[Serializable]
public class CommandArg
{
private NameValueCollection _paramCollection= new NameValueCollection();
public NameValueCollection ParamCollection
{
get
{
return _paramCollection;
}
set
{
_paramCollection=value;
}
}
}
In this CommandArg class, we have a NameValueCollection (ParamCollection), which will wrap all of the command parameters (coming from the GUI) and their names, so that we can use them in the Service Interface layer.
Next, we will look at the CommandResults class, which will wrap the results after the command is executed, and then these results can be used by the UI layer.
[Serializable]
public class CommandResult
{
private object _scalarResult;
private string _errorMessage="";
public string ErrorMessage
{
get
{
return _errorMessage;
}
set
{
_errorMessage=value;
}
}
public object ScalarResult
{
get
{
return _scalarResult;
}
set
{
_scalarResult=value;
}
}
}
This CommandResult class defines two properties:
-
ErrorMessage: to wrap any error while the command is executed by the lower layers. This error can then be displayed accordingly in the GUI. -
ScalarResult: this property will wrap the results of the command execution in an object and then send this object to the UI layer.
Both of these objects would be common to both the GUI and the Service Interface layers, which are used to pass data along these layers. We can also use Data Transfer Objects here.
Now, we will see what the actual Command class that executes a command looks like:
public class GetCustomerCmd : ICommand
{
public CommandResult Execute(CommandArg cmdArg)
{
CommandResult cmdResult = CustomerServiceInterface. GetCustomers(cmdArg);
return cmdResult;
}
}
In the above GetCustomerCmd class, the Execute method calls the Service interface object (we will define this in the next section), which in turn handles the business logic calls and returns the result in the Command Result object. The service interface method will refer to the BL layer, and use the BL objects to return the results. Here is a sample implementation:
public class CustomerServiceInterface
{
public CommandResult GetCustomers (CommandArg cmdArg)
{
CommandResult cmdResult = new CommandResult();
CustomerCollection customers = new CustomerCollection(); cmdArg = customer.FindCustomers(cmdArg);
return cmdResult;
}
}
The service method above uses the CustomerCollection class from the BL layer, and calls the FindCustomer method by passing in the arguments. The FindCustomer method returns a list of Customer records. This list is returned by wrapping it in the CommandResult class object.
[edit] Creating the Command Factory
How will our interface know which command to execute? For this, we take the help of the Factory design pattern, and create a CommandFactory class. This class takes eventName as an argument and, based on this string, it creates the appropriate command.
Now, we will see how the CommandFactory class is implemented:
public class CommandFactory
{
public static ICommand CreateCommand(string eventName)
{
switch(eventName)
{
#region Customer Related
case "GetCustomer":
return new GetcustomerCmd();
//other cases
...
}
}
}
Here, we are returning the actual command based on the string EventName. Based on this string parameter, the Factory class returns a concrete command object to the caller. To make it more flexible, instead of using strings hard-coded in the code we can put them in an XML file, and load that file to create appropriate command objects.
[edit] Tying it all up with the GUI
In the Addcustomer.aspx GUI page code behind the file, we can simply use the following code to create a new customer:
ICommand command;
// Call Create Command on CommandFactory passing EventName
command = CommandFactory.CreateCommand("GetCustomer");
CommandArg carg = new CommandArg();
CommandResults results = null;
carg.Add("customerID", "1");
if (command !=null)
{
// Call Execute Method on Command Object.
results = command.Execute(cmdArg); }
//bind results any data control or handle it
So we can clearly see that the GUI doesn't know anything about the Business Layer. All it does is create an instance of the command object by passing the required event name (GetCustomer) and then executing that command by passing arguments. The GUI doesn't need to know who handles the command and how the internal processing takes place. It just passes the required arguments on to the Command class, which then executes the command by talking to the business layer interface object.
This was just one example of how we can use the Command pattern to abstract the method invocation so that the caller does not need to worry about how the command will actually be executed. The example we studied is just one of the ways in which we can implement this pattern. However, each situation will need a different implementation, based on the actual needs although the basic principle would be same.
[edit] Additional References
- For instructions on Installing ASP.NET, click here
- For instructions on Customizing and Extending the ASP.NET MVC Framework, click here
[edit] Source
The source of this content is Chapter 6: Design patterns in ASP.NET 3.5 of ASP.NET 3.5 Application Architecture and Design by Vivek Thakur (Packt Publishing, 2008).
Logo Designby ThemesWiki.org Kevin Josh 2010
Executive Editor Sean Lopez own : SEO Company and provider of Link Building Services and SEO Services
And Like Costumes and Halloween Costumes
And Like The Global Information Network and Global Information Network
