Introduction
While I was developing a new web application in our company, I found that every entity in the system had some shared fields that held data about when and who saved that entity into the system. Basically, those fields were CreationDate
, CreatedBy
, LastModificationDate
, LastModificationBy
and EntityId
for sure. There are multiple approaches to solve this situation:
- In every user control, include these fields and reflect data to them directly. However, this would force you to write more duplicated code and would make update and maintenance more complicated and error-frequent processes. Imagine that you had 10 controls; this means you you'd need to do 10 updates if any update were needed in the entity details.
- Create a user control called
details
and place those fields in it, accessing those fields directly from each entity screen. Although this would ease maintenance and updates, this approach has a disadvantage in that those controls would now be tightly coupled. Applications with tightly coupled classes tend to be brittle and difficult to maintain, because changes in one class could affect all the tightly coupled classes. The problem: how can user controls -- objects -- notify other controls –- objects -- of state changes without being dependent on their classes? - Use the observer pattern and create loosely coupled controls to communicate between each other without depending on their classes.
In this article, I will show how to build a simple user control that implements the observer pattern to handle updates and pass notifications around, which is done in order to update a set of objects when some important event has occurred.
Background
The observer pattern is categorized under Behavioral Patterns. Behavioral Patterns are those patterns which are concerned with algorithms and the assignment of responsibilities between objects. Behavioral patterns describe not just patterns of objects or classes, but also the patterns of communication between them.
So, What is the Observer Pattern?
The observer design pattern should, “Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.”
What is the Subject? What is the Observer?
Subject: the object which will frequently change its state and upon which other objects depend.
Observer: the object which depends on a subject and updates according to its subject's state.
Here’s how it works: an observer signs up to receive notifications of changes to the subject.
A second observer can register itself, too. In fact, a subject may have any number of dependent observers. Observers are not aware of the presence of each other.
When a certain event occurs, all observers are notified.
An observer can unregister itself from a subject so that it will never receive notifications from the subject.
With this generic way of communicating between the subject and observers, collaborations can be built dynamically instead of statically. The code is now much more separate, and thus easier to maintain and reuse. There is no direct dependency between subject class and observer class.
Using the Code
In our example, Address_UC
is a ConcreteSubject
and RecordDetails_UC
is one of its ConcreteObserver
s. So, when the domain object associated with the Address_UC
class has changed, the RecordDetails_UC
class will be notified and will handle this event in a proper way. I will show how to explicitly implement the observer pattern another time by using delegates and events.
Implementing Subject Class
You can implement the Subject
class as a super class or abstract class. However, due to the limitations in some languages like C# and VB.NET, that class can inherit only once. This will cause a problem because most domain objects are inherits from domain objects. So, our subject class will be implemented using an interface. Remember that class can implement many interfaces.
public interface ISubject
{
List<IObserver> ObserversList {get;}
void AttachObserver(IObserver observer);
void DeAttachObserver(IObserver observer);
void NotifyObservers();
}
Implementing the Observer Class
public interface IObserver
{
void Updateobject(ISubject subject);
}
The observer class is very simple and contains a method that allows subjects to notify it. All we need to know is how to implement ISubject
methods in our ConcreteSubject
Address_UC
. It's as simple as this:
#region ISubject Members
public List<ObserverTutorial.App_Code.IObserver> ObserversList
{
get { return this.m_ObserversList; }
}
public void AttachObserver(ObserverTutorial.App_Code.IObserver observer)
{
this.ObserversList.Add(observer);
}
public void DeAttachObserver(ObserverTutorial.App_Code.IObserver observer)
{
this.ObserversList.Remove(observer);
}
public void NotifyObservers()
{
foreach (App_Code.IObserver observer in this.ObserversList )
{
observer.Updateobject(this);
}
}
#endregion
Note that the NotifyObservers
method will be called form another method in Address_UC
when a certain event occurs, something like after a user saves domain object data:
protected void btnOk_Click(object sender, EventArgs e)
{
bool result = SaveEntityData();
if (result)
{
this.NotifyObservers();
}
}
In the ConcreteObserver
RecordDetails_UC
, the implementation of the IObserver
lonely method Updateobject
allows Address_UC
to notify RecordDetails_UC
that it has changed. It is passing itself as a parameter so that RecordDetails_UC
can update its state, too, in a proper way. Firstly, however, RecordDetails_UC
needs to register itself with Address_UC
:
ISubject Subject = this.Parent as ISubject;
if (Subject != null)
{
Subject.AttachObserver(this);
}
Points of Interest
The Microsoft .NET Framework defines the notion of delegates and events to accomplish the observer role. Therefore you would rarely implement the observer pattern explicitly in .NET, but should use delegates and events instead. I attached an implementation of this sample using delegates and events, too.
History
- 5 February, 2008 -- Original version posted