Reader Level:
ARTICLE

Observer and .NET event delegates

Posted by Filip Bulovic Articles | C# Language December 17, 2001
The purpose of this article is to try to introduce observer pattern and compare it to .NET event delegate handling of notifications.
  • 0
  • 0
  • 25696
Download Files:
 

The purpose of this article is to try to introduce observer pattern and compare it to .NET event delegate handling of notifications. Purpose of Observer pattern also known as Dependents and Publish-Subscribe is solution of following problem: Define a one-to many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically. In the nutshell there must be class to be observed and class which is going to observe it. First class should implement Attach, Detach and Notify methods and also some container where references to observers will be stored. Observer must expose Update method (corresponds to event handler in .NET) whose purpose is to be called by Notify and to inform observer about changes of observed object. Further, both of these classes will be the base for concrete implementation (to improve reusability and simplify implementation) or to put all that in UML:

Please note that Observable is also known as Subject in literature. Of course instead of Get and Set we will use more elegant C# coding way i.e. property:

public string State
{
get
{
return Data;
}
set
{
Data =
value;
}
}

What else participants must do:

ConcreteObservable

It stores state of interest to ConcreteObserver objects.
It must send a notification to its observers when its state changes.

ConcreteObserver

It must maintain a reference to a ConcreteObservable object.
It stores state that should stay consistent with the Observable's.
And finally implements the Observer updating interface to keep its state consistent with the Observable's.

In .NET you are allowed to implement polymorphism in three ways through inheritance, interface or abstract class. I will use interface for Observer. This is my interpretation of Observer pattern:

using System;
using System.Collections;
class
Observable
{
protected ArrayList observers = new ArrayList();
public void Attach(Observer o)
{
observers.Add(o);
o.ObservedObject =
this;
}

public
void Detach(Observer o)
{
observers.Remove(o);
}
public void Notify()
{
foreach (Observer o in observers)
{
o.Update();
}
}
}
class ConcreteObservable : Observable
{
private string Data;
public string State
{
get
{
return Data;
}
set
{
Data =
value;
Notify();
}
}
}
interface Observer
{
void Update();
Observable ObservedObject{
get;set;}
}
class ConcreteObserver : Observer
{
private string observerName;
private ConcreteObservable co;
public
Observable ObservedObject
{
get
{
return (Observable)co;
}
set
{
co=(ConcreteObservable)
value;
}
}

public
ConcreteObserver(string name)
{
observerName = name;
}
public void Update()
{
Console.WriteLine("Notifying {0} new value is {1}",
observerName, co.State);
}
}
public class Client
{
public static void Main()
{
ConcreteObservable o =
new ConcreteObservable();
ConcreteObserver Observer1 =
new ConcreteObserver("Observer 1");
ConcreteObserver Observer2 =
new ConcreteObserver("Observer 2");
o.Attach(Observer1);
o.Attach(Observer2);
o.State="Hello World!";
o.Detach(Observer2);
o.State="Another change!";
o.Attach(Observer2);
o.State="Have a nice day!";
}
}

Observer pattern is a very good and robust solution when applied as a design technique to software construction. Unfortunately it also has a number of limitations if we are talking about evolution, flexibility and code reuse. Main characteristic of this pattern is loose coupling between Observables and Observers which makes possible for both of them to be extended and reused individually. To explain that further, Observable is not aware of concrete class of any Observer so ConcreteObserver can be extended at will under condition that it implements (or inherits from) Observer. On the other hand, observers have to obey the communication protocol (Update() method) but they also have to know the ConcreteObservable which they observe. It might be the case that for new requirements (for example more than one observation of state on ConcreteObservable) another communication protocol is needed.

In my exercise I was guided with the idea to compare Observer and .NET way of doing notifications through events and there is the same done in .NET using delegates:

EventListener will receive object to be observed through appropriate method (actually public property get-set) and name (so that we can differentiate it from other observers) will be received through constructor. EventListener will monitor changes of Storage.

using System;
public delegate void ChangedEventHandler(object sender, EventArgs e);
class GenericContainer
{
public event ChangedEventHandler Changed;
protected virtual void OnChanged(EventArgs e)
{
if (Changed != null)
Changed(
this, e);
}
}
class ConcreteContainer:GenericContainer
{
private string storage;
public string Storage
{
get
{
return storage;
}
set
{
storage =
value;
OnChanged(EventArgs.Empty);
}
}
}
class EventListener
{
private ConcreteContainer Data;
private string MyName;
public GenericContainer ObservedObject
{
get
{
return (GenericContainer) Data;
}
set
{
Data = (ConcreteContainer)
value;
}
}

public
EventListener(string name)
{
MyName = name;
}

public
void Attach()
{
Data.Changed +=
new ChangedEventHandler(DataChanged);
}

private
void DataChanged(object sender, EventArgs e)
{
Console.WriteLine("Notification to {1} new value is {0}",Data.Storage,MyName);
}

public
void Detach()
{
Data.Changed -=
new ChangedEventHandler(DataChanged);
}
}
class Client
{
public static void Main()
{
ConcreteContainer data =
new ConcreteContainer();
ventListener listener =
new EventListener("Listener 1");
listener.ObservedObject=data;
EventListener zorglup =
new EventListener("Listener 2");
zorglup.ObservedObject=data;
listener.Attach();
zorglup.Attach();
data.Storage="Hello World!";
zorglup.Detach();
data.Storage="Have a nice day!";
zorglup.Attach();
data.Storage="Another change!";
}
}

What is different? First there is no need for observed object to store list of observers and Notify() is gone, it will be done by delegate; second observer now does attaching and detaching and third observer doesn't expose Update() method anymore. What is the same? Observer still must know observed object. What things look like if we are talking about evolution, flexibility and code reuse? Coupling is more loose than before and it looks to me that changes are easier. To be honest improvement is not enormous (two pieces of code are very similar) but it is evident.

And for the end if we are using interfaces, attributes and other stuff from COM arsenal isn't it just another form of COM, what is a gain?

We are not forced to stick with famous design error that all interfaces must descend from mother of all interfaces IUnknown. We have things which are helpful in real world development, we have full OO support if we like to pursue theoretically correct models. To cut a long story short .NET really offers very wide variety of tools to wide variety of developers, where level of knowledge might be again very different.

COMMENT USING

Trending up