Friday, May 28, 2010

Using the Observer Pattern

At work we are reading “Head First Design Patterns” and discussing the “Observer Pattern” and some people wanted to see a code example.
The first part is the the class that manages the observer classes
   1: public class EventManager<T>
   2: { 
   3:     private Dictionary<string, ISubscriber<T>> _subscribers = new Dictionary<string, ISubscriber<T>>();
   4:     public Dictionary<string,ISubscriber<T>> Subscribers
   5:     {
   6:         get { return _subscribers; }
   7:         set { _subscribers = value; }
   8:     }
   9:  
  10:     public void Notify(T newState)
  11:     {
  12:         foreach (var observer in Subscribers.Values)
  13:         {
  14:             observer.State = newState;
  15:             observer.Update();
  16:         }
  17:     }
  18: }
In this example I created a generic class that has two basic parts a collection of subscribers or observers and a method that loops though and updates the state of the subscribers.  I used a Dictionary<string,ISubscriber<T>> to make it easy to manage my subscribers based on a key, and because ISubscriber<T> is also a generic its a very flexible and strongly typed way to work with the subscribers.

Next let’s take a look at ISubscriber<T> interface
   1: public interface ISubscriber<T>
   2: {
   3:     T State { get; set; }
   4:     void Update();
   5: }
it’s that simple, I use generics to determine the state and the input type for the update method, here is an example implementation
   1: public class TweetLocation : ISubscriber<Location>
   2: {
   3:     public ITwitter TwitterInterfacer = new FakeTwitterClient();
   4:     public Location State { get; set; }
   5:  
   6:     public void Update()
   7:     {
   8:         TwitterInterfacer.TweetMessage(string.Format("currently at ({0},{1})",State.Lat,State.Lon));
   9:     }
  10: }
TweetLocation is observing Location information and calls a Twitter class to Tweet the new location when updated, for something a little more complex
   1: public class SaveLocation : ISubscriber<Location>
   2: {
   3:     public ILocationReposigtory LocationRepository = new FakeLocationRepository();
   4:     public static EventManager<string> ErrorHandler = new EventManager<string>();
   5:  
   6:     public Location State{get;set;}
   7:  
   8:     public SaveLocation()
   9:     {
  10:         ErrorHandler.Subscribers.Add("ErrorMailer",new ErrorMailer());
  11:     }
  12:  
  13:     public void Update()
  14:     {
  15:         bool success = LocationRepository.SaveLocation(State);
  16:         if (!success)
  17:         {
  18:             ErrorHandler.Notify("Failed to save location");
  19:         }
  20:     }
  21: }
SaveLocation is basically the same as TweetLocation except it has it’s own EventManager to handle errors from failed attempts at saving the location.

Finally here is an example of actually using it
   1: class Program
   2: {
   3:     public static EventManager<Location> LocationHandler = new EventManager<Location>();
   4:  
   5:     static void Main(string[] args)
   6:     {
   7:         LocationHandler.Subscribers.Add("Repository",new SaveLocation());
   8:         LocationHandler.Subscribers.Add("Twitter",new TweetLocation());
   9:  
  10:         Location currentLocation = new Location(){Lat = 111,Lon = -121};
  11:  
  12:         LocationHandler.Notify(currentLocation);
  13:  
  14:         Console.ReadKey();
  15:  
  16:         currentLocation.Lon = 1211;
  17:         LocationHandler.Subscribers.Remove("Twitter");
  18:         LocationHandler.Notify(currentLocation);
  19:  
  20:         Console.ReadKey();
  21:         
  22:         
  23:     }
  24: }
This is a simple console app that demonstrates adding subscribers, updating state, and removing a subscriber. 

A real world example of using “Observer Pattern” is a service bus like NServiceBus or MassTransit, where an event driven application needs to send messages to one or more subscribers.

As always here is a link to the project source.

No comments: