62 msdn magazine Aspect-Oriented Programming
- You could inherit a new class and add the new functionality,
but that may result in many new classes. For example, let’s
say you have a repository class for create, read, update and
delete (CRUD) database operations and you want to add
auditing. Later, you want to add data validation to be sure
the data is being updated correctly. Aft er that, you might
also want to authenticate the access to ensure that only
authorized users can access the classes. These are big
issues: You could have some classes that implement all three
aspects, and some that implement only two of them or even
only one. How many classes would you end up having?
- You can “decorate” the class with the aspect, creating a new
class that uses the aspect and then calls the old one. Th at way,
if you need one aspect, you decorate it once. For two aspects,
you decorate it twice and so on. Let’s say you order a toy (as
we’re all geeks, an Xbox or a smartphone is OK). It needs a
package for display in the store and for protection. Th en, you
order it with gift wrap, the second decoration, to embellish the
box with tapes, stripes, cards and gift paper. Th e store sends the
toy with a third package, a box with Styrofoam balls for pro-
tection. You have three decorations, each one with a diff erent
functionality, and each one independent from one another.
You can buy your toy with no gift packaging, pick it up at the
store without the external box or even buy it with no box (with a
special discount!). You can have your toy with any combination
of the decorations, but they don’t change its basic functionality.
Now that you know about the Decorator pattern, I’ll show how
to implement it in C#.
First, create an interface IRepository
public interface IRepository<T>
{
void Add(T entity);
void Delete(T entity);
void Update(T entity);
IEnumerable<T> GetAll();
T GetById(int id);
}
Implement it with the Repository
Use the Repository
the elements of the Customer class:
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
public string Address { get; set; }
}
Th e program could look something like Figure 2.
When you run this code, you’ll see something like Figure 3.
Imagine your boss asks you to add logging to this class.
You can create a new class that will decorate IRepository
It receives the class to build and implements the same
interface, as shown in Figure 4.
Th is new class wraps the methods for the decorated
class and adds the logging feature. You must change the
code a little to call the logging class, as shown in Figure 5.
static void Main(string[] args)
{
Console.WriteLine("***\r\n Begin program - logging with decorator\r\n");
// IRepository<Customer> customerRepository =
// new Repository<Customer>();
IRepository<Customer> customerRepository =
new LoggerRepository<Customer>(new Repository<Customer>());
var customer = new Customer
{
Id = 1,
Name = "Customer 1",
Address = "Address 1"
};
customerRepository.Add(customer);
customerRepository.Update(customer);
customerRepository.Delete(customer);
Console.WriteLine("\r\nEnd program - logging with decorator\r\n***");
Console.ReadLine();
}
Figure 5 The Main Program Using The Logger Repository
class DynamicProxy<T> : RealProxy
{
private readonly T _decorated;
public DynamicProxy(T decorated)
: base(typeof(T))
{
_decorated = decorated;
}
private void Log(string msg, object arg = null)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(msg, arg);
Console.ResetColor();
}
public override IMessage Invoke(IMessage msg)
{
var methodCall = msg as IMethodCallMessage;
var methodInfo = methodCall.MethodBase as MethodInfo;
Log("In Dynamic Proxy - Before executing '{0}'",
methodCall.MethodName);
try
{
var result = methodInfo.Invoke(_decorated, methodCall.InArgs);
Log("In Dynamic Proxy - After executing '{0}' ",
methodCall.MethodName);
return new ReturnMessage(result, null, 0,
methodCall.LogicalCallContext, methodCall);
}
catch (Exception e)
{
Log(string.Format(
"In Dynamic Proxy- Exception {0} executing '{1}'", e),
methodCall.MethodName);
return new ReturnMessage(e, methodCall);
}
}
}
Figure 7 The Dynamic Proxy Class
Figure 6 Execution of the Logging Program with a Decorator