Publisher/Subscriber Pattern With Event /Delegate and EventAggregator

Publisher/Subscriber pattern

The Publisher/Subscriber pattern is one of the variations of the Observer designer pattern introduced by the GOF in software devlopment. In the Publisher/Subscriber pattern a publisher (entiry responsible for publishing a message) publishes a message and there are one or more Subscribers (entity subsribing, in other words intested in a message of a specified message type) who capture the published message. The following image desribes the senario of the publisher and subscriber pattern where the Publisher publishes two types of messages (MessageA and MessageB) and Subscribers to the messages receive the messages they are subscribed to (Subscriber1 captures MessageA and Subscriber2 and Subscriber3 captures MessageB).

publisher

To understand this consider a real-life scenario where Mobile operators are sending messages to their customers.

Mobile operators

As in the preceding image a Mobile operator publishes (broadcasts) messages (a message of a Cricket score and a message of latest news) and the messages are captured by the customer cells subscribing to the messages (Customer1 and Customer2 captures the Cricket score messages and Customer3 and Customer4 captures the latest news messages).

Implementation with Event

One way to do the Publisher/Subscriber pattern in an application is to use events and delegated, in other words using a framework. The following is a detailed description of a publisher/subscriber implementation
message. The following is a class that represents a message that is published by a Publisher and is captured by an interested Subscriber.

  1. public class MessageArgument<T> : EventArgs  
  2. {  
  3.     public T Message { get;  set; }  
  4.     public MessageArgument(T message)  
  5.     {  
  6.         Message = message;  
  7.     }  
  8. }  
In technical terms a MessageArgument is a generic class so an instance of this class can be of any type that is represented by a T template type.

Publisher: As already described above in the definition, a Publisher is responsible for publishing messages of various types.
  1. public interface IPublisher<T>  
  2. {  
  3.     event EventHandler<MessageArgument<T>> DataPublisher;  
  4.     void OnDataPublisher(MessageArgument<T> args);  
  5.     void PublishData(T data);  
  6. }  
  7.    
  8. public class Publisher<T> : IPublisher<T>  
  9. {  
  10.     //Defined datapublisher event  
  11.     public event EventHandler<MessageArgument<T>> DataPublisher;  
  12.    
  13.     public void OnDataPublisher(MessageArgument<T> args)  
  14.     {  
  15.         var handler = DataPublisher;  
  16.         if (handler != null)  
  17.             handler(this, args);  
  18.     }  
  19.    
  20.    
  21.     public void PublishData(T data)  
  22.     {  
  23.         MessageArgument<T> message = (MessageArgument<T>)Activator.CreateInstance(typeof(MessageArgument<T>), new object[] { data });  
  24.         OnDataPublisher(message);  
  25.     }  
  26. }  
Technically Publisher is a generic class that inherits the IPublisher interface. Since Publisher is a generic class an instance of it can be of any type represented by a T template type. A Publisher instance is created of a selected type and publishes that type of message only. To publish a different type of message a different type of Publisher instance must be created. To understand better read the following about how to use a publisher in client class code.

The Publisher class provides the event DataPublisher that a subscriber attaches to listen for messages.

PublishData is a publisher class method that publishes data to Subscribers.

Subsriber: Subscriber captures messages of the type it is interested in.
  1. public class Subscriber<T>  
  2. {  
  3.     public IPublisher<T> Publisher { getprivate set; }  
  4.     public Subscriber(IPublisher<T> publisher)  
  5.     {  
  6.         Publisher = publisher;  
  7.     }  
  8. }  
Technically a Subscriber is a generic class that allows creation of multiple instances of a subscriber and each subscriber subscribes to the messages it is interested in using a publisher.

A Subscriber passes an instance of a specific type of publisher to capture messages published by that Publisher.

How it works
  1. public class Client  
  2. {  
  3.     private readonly IPublisher<int> IntPublisher;  
  4.     private readonly Subscriber<int> IntSublisher1;  
  5.     private readonly Subscriber<int> IntSublisher2;  
  6.    
  7.     public Client()  
  8.     {  
  9.         IntPublisher = new Publisher<int>();//create publisher of type integer  
  10.    
  11.         IntSublisher1 = new Subscriber<int>(IntPublisher);//subscriber 1 subscribe to integer publisher  
  12.         IntSublisher1.Publisher.DataPublisher += publisher_DataPublisher1;//event method to listen publish data  
  13.    
  14.         IntSublisher2 = new Subscriber<int>(IntPublisher);//subscriber 2 subscribe to interger publisher  
  15.         IntSublisher2.Publisher.DataPublisher += publisher_DataPublisher2;//event method to listen publish data  
  16.    
  17.         IntPublisher.PublishData(10); // publisher publish message  
  18.     }  
  19.    
  20.     void publisher_DataPublisher1(object sender, MessageArgument<int> e)  
  21.     {  
  22.         Console.WriteLine("Subscriber 1 : " + e.Message);  
  23.     }  
  24.    
  25.     void publisher_DataPublisher2(object sender, MessageArgument<int> e)  
  26.     {  
  27.         Console.WriteLine("Subscriber 2 : " + e.Message);  
  28.     }  
  29. }  
As you can see in the preceding code, Client is a class that creates a publisher and subscriber. The Client class creates a Publisher of integer type (in practice you can create any time of publisher) and creates two Subscriber classes that subscribe to the publisher message by attaching a DataPublisher event that is provided by the Publisher class.

So when you create an instance of the Client class you will receive the following output:

output

So as per the output the Publisher of integer type publishes the message "10" and two Subscribers subscribing to the publisher capture and display the message to output.

In a practical scenario, in other words in an actual application, one must create all the publishers during the application start, in other words at the entrypoint of the app and pass the instances of publishers when creating subscribers.

For example in a Windows application create a publisher in the Main() method and in a Web Application create a publisher in the Appication_Start method of Global.asax use Dependency Injection to register your publisher and use a container to create it when needed.

Once yu have created them you can pass the publisher in the subscriber as done in the preceding client class code.

Implementation with EventAggregator

EventAggregator: by the name one can easily say it aggregates events. An Publisher/Subscriber EventAggregator woks as a HUB whose task is to aggregate all the published messages and send the message to the interested subscribers.

EventAggregator

As you can see in the preceding image, an EventAggregator comes as a HUB between a publisher and a subscriber. It works like this:

 

  1. Publisher publishes a message.

  2. EventAggregator receives a message sent by publishers.

  3. EventAggregator gets a list of all subscriber interested messages.

  4. EventAgregator sends the messages to the interested subscriber.

EventAggregator Implementation

SubScription:
It is a class to create subscription tokens. When a Subscriber subscribes to interested message types via EventAggregator the EventAggregator returns a subscription token that is further used by the subscriber to keep track of its subscriptions.

  1. //Does used by EventAggregator to reserve subscription  
  2. public class Subscription<Tmessage> : IDisposable  
  3. {  
  4.     public Action<Tmessage> Action { getprivate set; }  
  5.     private readonly EventAggregator EventAggregator;  
  6.     private bool isDisposed;  
  7.     public Subscription(Action<Tmessage> action, EventAggregator eventAggregator)  
  8.     {  
  9.         Action = action;  
  10.         EventAggregator = eventAggregator;  
  11.     }  
  12.    
  13.     ~Subscription()  
  14.     {  
  15.         if (!isDisposed)  
  16.             Dispose();  
  17.     }  
  18.    
  19.     public void Dispose()  
  20.     {  
  21.         EventAggregator.UnSbscribe(this);  
  22.         isDisposed = true;  
  23.     }  
  24. }  
EventAggregator
  1. public class EventAggregator  
  2. {  
  3.     private Dictionary<Type, IList> subscriber;  
  4.    
  5.     public EventAggregator()  
  6.     {  
  7.         subscriber = new Dictionary<Type, IList>();  
  8.     }  
  9.    
  10.     public void Publish<TMessageType>(TMessageType message)  
  11.     {  
  12.         Type t = typeof(TMessageType);  
  13.         IList actionlst;  
  14.         if (subscriber.ContainsKey(t))  
  15.         {  
  16.             actionlst = new List<Subscription<TMessageType>>(subscriber[t].Cast<Subscription<TMessageType>>());  
  17.    
  18.             foreach (Subscription<TMessageType> a in actionlst)  
  19.             {  
  20.                 a.Action(message);  
  21.             }  
  22.         }  
  23.     }  
  24.    
  25.     public Subscription<TMessageType> Subscribe<TMessageType>(Action<TMessageType> action)  
  26.     {  
  27.         Type t = typeof(TMessageType);  
  28.         IList actionlst;  
  29.         var actiondetail = new Subscription<TMessageType>(action,this);  
  30.    
  31.         if (!subscriber.TryGetValue(t, out actionlst))  
  32.         {  
  33.             actionlst = new List<Subscription<TMessageType>>();  
  34.             actionlst.Add(actiondetail);  
  35.             subscriber.Add(t, actionlst);  
  36.         }  
  37.         else  
  38.         {  
  39.             actionlst.Add(actiondetail);  
  40.         }  
  41.    
  42.         return actiondetail;  
  43.     }  
  44.    
  45.     public void UnSbscribe<TMessageType>(Subscription<TMessageType> subscription)  
  46.     {  
  47.         Type t = typeof(TMessageType);  
  48.         if (subscriber.ContainsKey(t))  
  49.         {  
  50.             subscriber[t].Remove(subscription);  
  51.         }  
  52.     }  
  53.    
  54. }  
In the preceding code  

    Dictionary<Type, IList> subscriber: is a dictionary in which Type is the type of message and IList is a list of actions. So it holds a list of actions mapped to specific Message Types.

    public void Publish<TMessageType>(TMessageType message):
    is a method to publish messages. As in the code, this method receives messages as input then filters out a list of all subscribers by message type and publishes messages to the subscriber.

    public Subscription<TMessageType> Subscribe<TMessageType>(Action<TMessageType> action): is a method for subsribing to interested message types. As in the code this method recevies an Action delegate as input. It maps an Action to a specific MessageType, in other words it creates an entry for message type if not present in the dictionary and maps a Subscription object (that waps an Action) to a message entry.

    public void UnSbscribe<TMessageType>(Subscription<TMessageType> subscription):
    is a method for unsubscribing from a specific message type. It receives a Subscription object as input and removes an object from the dictionary.

How To Use
  1. static void Main(string[] args)  
  2. {  
  3.     EventAggregator eve = new EventAggregator();  
  4.     Publisher pub = new Publisher(eve);  
  5.     Subscriber sub = new Subscriber(eve);  
  6.   
  7.     pub.PublishMessage();  
  8.   
  9.     Console.ReadLine();  
  10.   
  11. }  
The preceding code shows, first create an instance of EventAggregator that passes as an argument to a publisher and subscriber, then the publisher publishes message.

Publisher: Code of Publisher class that shows how a publisher publishes a message using EventAggregator.
  1. public class Publisher  
  2. {  
  3.     EventAggregator EventAggregator;  
  4.     public Publisher(EventAggregator eventAggregator)  
  5.     {  
  6.         EventAggregator = eventAggregator;  
  7.     }  
  8.   
  9.     public void PublishMessage()  
  10.     {  
  11.         EventAggregator.Publish(new Mymessage());  
  12.         EventAggregator.Publish(10);  
  13.     }  
  14. }  
Subscriber: Code of the Subscriber class that shows a subscriber subscribing to messages that it is interested in using EventAggregator.
  1. public class Subscriber  
  2. {  
  3.     Subscription<Mymessage> myMessageToken;  
  4.     Subscription<int> intToken;  
  5.     EventAggregator eventAggregator;  
  6.    
  7.     public Subscriber(EventAggregator eve)  
  8.     {  
  9.         eventAggregator = eve;  
  10.         eve.Subscribe<Mymessage>(this.Test);  
  11.         eve.Subscribe<int>(this.IntTest);  
  12.     }  
  13.    
  14.     private void IntTest(int obj)  
  15.     {  
  16.         Console.WriteLine(obj);  
  17.         eventAggregator.UnSbscribe(intToken);  
  18.     }  
  19.    
  20.     private void Test(Mymessage test)  
  21.     {  
  22.         Console.WriteLine(test.ToString());  
  23.         eventAggregator.UnSbscribe(myMessageToken);  
  24.     }  
  25. }  
Output

console


Note:
In practical scenarios, in other words in an actual application, one must create an EventAggregator at the application start point, in other words at the entrypoint of the app and pass an instance of a publisher and subscriber.

For example in a Windows application create an EventAggregator in the Main() Method as in the preceding example code then in a Web Application create a publisher in the Appication_Start method of Global.asax or use Dependency Injection to register your publisher and use a container to create when needed.

Event/Delegate Vs. EventAggregator

Difference between Event/Delegate and EventAggregator is:

table

Conclusion

In my point of view, an Event/Delegate is easy to implement and good for small projects or in a project where there are fewer Publishers and Subscribers. An EventAggregator is suitable for large projects or projects with a large number of Publishers and Subscirbers.

But I think it's always good to use EventAggregator because it offers loose coupling.

Note

This article represents my experience and my point of view. Please comment on this and provide your feedback.


Similar Articles