Observation pattern in C#
Observer design pattern in the most common used pattern in software world.Infact,in the real world also,there are many instances where we are following an observer pattern e.g. subscribing to newspaper edition,waking up in the morning to goto office when the alarm rings,etc.
Lets come straight to the intent – The intent of the pattern is to define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.Ok,lets begin to see its implementation – Observer pattern revolves around subject and observers. The subject is someone in whose state observers are interested. Taking the example forward,lets say we have a fictitious adventure company called GreatWhiteCompany which is into the business of adventure activities. I once did a rafting expedition through them and seeing their professionalism decided to subscribe to their newspaper.So,here I am the observer and needs to be notified whenever there is a good deal from the company.Now there can be other types of subscribers also who may not be that much interested but still wants to be in the know of things. They also wants to get notified.So lets start coding the classes.
Since ,we had identified the types of subject and observers,we can clearly define 2 interfaces – One interface would be for subjects and another would be for observers.Why interfaces here ?- The main purpose of interfaces is to decouple 2 types of objects.
public interface IAdventureCompany
{
void Register(IAdventurer adventurer);
void UnRegister(IAdventurer adventurer);
void Notify();
}
public interface IAdventurer
{
void Update();
}
The important thing to observe here is that the subject classes is interested only in the contract and not in the actual implementation since that is the responsibility of observers themselves.So lets see our subject class:
public class GreatWhiteCompany : IAdventureCompany
{
private ArrayList adventurers;
public GreatWhiteCompany()
{
adventurers = new ArrayList();
}
public void Register(IAdventurer adventurer)
{
adventurers.Add(adventurer);
}
public void UnRegister(IAdventurer adventurer)
{
adventurers.Remove(adventurer);
}
public void Notify()
{
foreach (IAdventurer adventurer in adventurers)
{
adventurer.Update();
}
}
}
The code is very simple to understand.It is to be understood here that we are following a Push model where subject is pushing the updates to the observers.The pattern can also be implemented using the Pull model where each observer requests the information from the subject.Lets see the implementation now :
//Observer implementation using traditional method
GreatWhiteCompany raftingSolutions = new GreatWhiteCompany();
EnthusiasticAdventurer enthusiasticAdventurer = new EnthusiasticAdventurer();
raftingSolutions.Register(enthusiasticAdventurer);
AverageAdventurer averageAdventurer = new AverageAdventurer();
raftingSolutions.Register(averageAdventurer);
raftingSolutions.Notify();
Console.ReadLine();
raftingSolutions.UnRegister(averageAdventurer);
raftingSolutions.Notify();
Console.ReadLine();
Running this code will show how different adventurers react to the new scheme launched by our fictitious company.Also to be noted is that the AverageAdventurer wishes to be removed from the subscriber list and consequently he would not get the updates.
.NET framework provides a very neat method to implement the Observer pattern through delegates and events.
Behind the scenes,delegates and events do the same work i.e. A subject publishes an event to which any subscriber could attach to.This is done using delegates. GreateRaftingCompany greatRaftingCompany = new GreateRaftingCompany();
EnthusiasticObserver enthusiasticObserver = new EnthusiasticObserver();
//Lukka oneLukka = new Lukka();
//enthusiasticObserver.InformLukkaParty += oneLukka.Indulge;
AverageObserver averageObserver = new AverageObserver();
greatRaftingCompany.SchemeLaunched += enthusiasticObserver.LetsGoRafting;
greatRaftingCompany.SchemeLaunched += averageObserver.LetsDecide;
greatRaftingCompany.PublishNewScheme();
greatRaftingCompany.SchemeLaunched -= averageObserver.LetsDecide;
greatRaftingCompany.PublishNewScheme();
Console.ReadKey();
Basically,the subject class (in this case- GreateRaftingCompany) publishes an event – SchemeLaunched to which EnthusiasticObserver and AverageObserver subscribe.The event internally holds a delegate pointer pointing to all the event handlers which will be invoked when the event will be raised.
Eventually,event mechanism in .NET is doing the same things as our example above.Also it is to be noted that the event chaining is not limited to Subject to Observers.Its also possible that the observer himself is acting as subject and notifying some other observer.To illustrate this lets assume that the EnthusiasticObserver has some friends who have formed a secret group called Lukka gang and their only purpose in life is to indulge in adventure activities.Now when the rafting company notifies EnthusiasticObserver of the scheme he wants to further notify his Lukka gang.So here after
receiving notification ,he is acting as a subject and notifying his friends.Now it’s a simple matter of his friends in Lukka gang subscribing to the event he has published.
public class EnthusiasticObserver
{
public delegate void LukkaIndulgenceHandler(object sender,EventArgs e);
public event LukkaIndulgenceHandler InformLukkaParty;
private void OnGoodNews()
{
if (InformLukkaParty != null)
InformLukkaParty(this, null);
}
public void LetsGoRafting(object sender, EventArgs e)
{
Console.WriteLine("EnthusiasticObserver : We are ready to go whatever the costs");
OnGoodNews();
}
}
Hope this article will help in understating Observer design pattern and its implantation in the context of .Net framework.
The complete code is listed below:
public interface IAdventureCompany
{
void Register(IAdventurer adventurer);
void UnRegister(IAdventurer adventurer);
void Notify();
}
public interface IAdventurer
{
void Update();
}
public class EnthusiasticAdventurer : IAdventurer
{
public void Update()
{
Console.WriteLine("EnthusiasticAdventurer : Lets pack the bags and go.");
}
}
public class AverageAdventurer : IAdventurer
{
public void Update()
{
Console.WriteLine("AverageAdventurer : Lets work out the expenses and then decide.");
}
}
public class Lukka
{
public void Indulge(object sender, EventArgs e)
{
Console.WriteLine("Lukka : I am surely going to join");
}
}
public class GreatWhiteCompany : IAdventureCompany
{
private ArrayList adventurers;
public GreatWhiteCompany()
{
adventurers = new ArrayList();
}
public void Register(IAdventurer adventurer)
{
adventurers.Add(adventurer);
}
public void UnRegister(IAdventurer adventurer)
{
adventurers.Remove(adventurer);
}
public void Notify()
{
foreach (IAdventurer adventurer in adventurers)
{
adventurer.Update();
}
}
}
public class EnthusiasticObserver
{
public delegate void LukkaIndulgenceHandler(object sender,EventArgs e);
public event LukkaIndulgenceHandler InformLukkaParty;
private void OnGoodNews()
{
if (InformLukkaParty != null)
InformLukkaParty(this, null);
}
public void LetsGoRafting(object sender, EventArgs e)
{
Console.WriteLine("EnthusiasticObserver : We are ready to go whatever the costs");
OnGoodNews();
}
}
public class AverageObserver
{
public void LetsDecide(object sender, EventArgs e)
{
Console.WriteLine("AverageObserver : Lets decide what is our budget");
}
}
public class GreateRaftingCompany
{
public delegate void NewSchemeLaunchHandler(object obj, EventArgs e);
public event NewSchemeLaunchHandler SchemeLaunched;
private ArrayList adventurers;
public GreateRaftingCompany()
{
adventurers = new ArrayList();
}
public void PublishNewScheme()
{
Console.WriteLine("GreateRaftingCompany is giving an inaugral discount of 500 on a one day trip");
OnNewSchemeLaunched();
}
private void OnNewSchemeLaunched()
{
if (SchemeLaunched != null)
SchemeLaunched(this, null);
}
}
Comments
Post a Comment