Thursday, February 25, 2010

Design Patterns in asp.net

Observer Pattern in C#

The Observer Pattern is an often used design pattern in software development. The idea is one class (the publisher ) publishes an event (say a button click) and other objects can subscribe to that event and do something fancy when they hear about it.

The .NET framework uses delegates and events to follow the Observer pattern.

The example below allows a usercontrol (the publisher) embedded in an aspx page to tell the subscriber, in this case the aspx page when a button has been clicked.

So I can subscribe to the event BubbleClick and tell it that a new event will need to be notified when that event occurs, in this case it is a event called eventRejectRedemptionSubmit_Click in the aspx page.

Publisher

public event EventHandler BubbleClick;
protected void AnEventSuchAsButtonClick(object sender, eventArgs e)
{
if (BubbleClick != null)
{
BubbleClick(this, e);
}
}

Subscriber

protected override void OnInit(EventArgs e)
{
base.OnInit(e);
PromotionRejectionReason1.BubbleClick += new EventHandler(eventRejectRedemptionSubmit_Click);
}

protected void eventRejectRedemptionSubmit_Click(object sender, EventArgs e)
{
PromotionApproveReceipts1.DisplayButtonsPlaceHolder = false;
}


The benefit of the Observer Pattern is if you want to add more subscribers to the BubbleClick event we can do so with out touching the code in the acsx control (the publisher)

Why use a delegate in C#

There are a lot of articles on the internet on how to use delegates in C#. Normally something along the lines of “They are type safe versions of c/c++ function pointers”. This explanation is correct but means nothing if you have no experience in C!

So here is an explanation of why one would use a delegate. (They are used in events too but let’s leave that for now).

Remember one of the key principals in OOP is CHANGE. So things change all the time, the less areas of code we have to change the less likely bugs will appear

Here is the issue without using a delegate

An error occurs in our program and we want to log it somewhere. At the start of the project we have a static class Logger that has one method


public static class Logger

{

public static void WriteLogToDatabase(string textToLog)

{

Console.WriteLine("Writing to database the text " + textToLog);

}

}

From the main program we call the method like

class Program

{

static void Main(string[] args)

{

Logger.WriteLogToDatabase("An error has occured again");

}

}

All well and good we have deployed the project and it works great

We have decided we need to write errors to XML as well. So now you have to change the class Logger (which we know works but will have to be tested again because we have altered it) to include the new method and change main program to call it, now we have:

public static class Logger

{

public static void WriteLogToDatabase(string textToLog)

{

Console.WriteLine("Writing to database the text " + textToLog);

}

public static void WriteLogToXML(string textToLog)

{

Console.WriteLine("Writing to XML the text " + textToLog);

}

}

From the main program we call the method like:

class Program

{

static void Main(string[] args)

{

Logger.WriteLogToDatabase("An error has occured again");

Logger.WriteLogToXML("An error has occured again");

}

}

Now we have only 2 methods and you have to alter the code in 2 places main.cs and Logger.cs. What would happen if you had to add more methods e.g. WriteLogToMobile, WriteLogToPrinter , WriteLogToSomeFutureDeviceThatHasNotBeenInvented, another 50 methods? Changing the code over and over, more bugs, more places to change code.

Now it gets worse, another developer has created some more logging methods in another class that you want to call in your Logger.cs class. Time to change the code yet again, pasting new code into your logger class.


A solution with delegates


What you could do is change your Logger class to contain a method that accepts a delegate as a parameter. So the new method gets a pointer to a method we want to use. So Logger.cs doesn’t care what the method is, as long as it follows the same signature of the delegate. That means Logger.cs never has to be changed, retested or redeployed.

public static class Logger

{

public static void WriteLog(WriteLogDelegate writeLogDelegate, string textToLog)

{

Console.WriteLine(writeLogDelegate() + textToLog);

}

}

Also you can add as many new methods in different classes as you like, that way you don’t even have to change your original methods. So I have:

public static class SomeLoggingMethods

{

public static string WriteLogToDatabase()

{

// code to write to database etc

return("Writing to database the text ");

}

public static string WriteLogToXML()

{

// code to write to xml etc

return("Writing to XML the text ");

}

}

And then a new programmer adds this:

public static class SomeOtherLoggingMethodsSomeoneElseHasWritten

{

public static string WriteLogToCoolNewDevice()

{

// code to write to database etc

return ("Writing to Cool New Device the text ");

}

}

The only changes to our original is in main.cs. The class Logger.cs is encapsulated and does nto need any editing to add new features to it.

static void Main(string[] args)

{

WriteLogDelegate writeLog = new WriteLogDelegate(SomeLoggingMethods.WriteLogToDatabase);

Logger.WriteLog(writeLog, " An error");

writeLog = new WriteLogDelegate(SomeOtherLoggingMethodsSomeoneElseHasWritten.WriteLogToCoolNewDevice);

Logger.WriteLog(writeLog, " An error");

}


Delegates are used for all sorts of reasons and this is just one of them.