The Observer Design Pattern allows you to have a publisher-subscriber
framework where a change to a publisher will notify all of its subscribers
automatically. The subscribers are
registered to the publisher so that when a change occurs in the publisher all
of the subscribers are notified. The publishers and the subscribers are decoupled
through the use of interfaces so that the development of each can vary
independently. |
1. The first are the subjects. They are the publishers. When a change occurs to a subject it should notify all of its subscribers.
2. The second are the observers. They are the subscribers. They simply listen to the changes in the subjects.
The subjects are the publishers and the observers are the
subscribers. It's just a different terminology. Below is the UML of
the Observer Design Pattern, the left part are the subjects, and the right part are the observers:
- The ISubject is the interface that all publishers implement and has the following properties and methods:
- observers -- List of observers that listen to the changes in the subject
- Attach(IObserver) -- Adds an observer to listen to changes in the subject
- Detach(IObserver) -- Remove an observer from listening changes in the subject
- Notify() -- Send updates to all the observers that subscribed to it
- The ConcreteSubject is the publisher class and it implements the ISubject interface. Besides the implementation of the ISubject interface it also has the subjectState variable:
- subjectState -- the variable that represents the state of the subject
- The IObserver is the interface that all subscribers implement and has the Update method:
- Update() -- update the subscriber and is called by the subject (publisher)
- The ConcreteObserver is the subscriber class and it implements the IObserver interface. Below are its variables and methods:
- observerState -- the variable that represents the state of the observer
- Update() -- update the state of the observer. Notice that the method simply assigns the observerState variable from the subject's state. Therefore when a change to the subject's state occurs, the observer's state will become the same as the subject's state.
Notice in the Update method of the ConcreteObserver we assign the observerState variable as the subject's state and we only have one observerState variable. This means that the observer pattern is a one-to-many relationship, where one subject can have many observers listening to the subject's change but not vice versa.
While there are many different ways to implement the observer pattern, such as using delegates and events or the IObserver<T>, the concepts are all the same. That is, the observers are registered to listen to the changes in the subject and are notified when the subject changes. For the purpose of demonstrating the concept of the observer pattern we will not dig into the technicalities of the multiple ways to implement the pattern, but simply show how the observer pattern works by demonstrating the concept.
Below are the implementation code and the output of the Observer Design Pattern. Notice that a change to the subject automatically updates all of its observers:
class Program
{
static void Main(string[] args)
{
//one subject and two observers
ISubject<string> subject = new ConcreteSubject<string>();
IObserver<string> observer1 = new ConcreteObserver<string>("observer1");
IObserver<string> observer2 = new ConcreteObserver<string>("observer2");
//register the observers to the subject
subject.Attach(observer1);
subject.Attach(observer2);
//a change to the subject automatically notifies all the observers
subject.SetState("stateX");
}
}
public interface ISubject<T>
{
List<IObserver<T>> Observers { get; }
void Attach(IObserver<T> i);
IObserver<T> Detach(IObserver<T> i);
void Notify();
T GetState();
void SetState(T state);
}
public class ConcreteSubject<T> : ISubject<T>
{
private List<IObserver<T>> Observers = new List<IObserver<T>>();
private T SubjectState;
List<IObserver<T>> ISubject<T>.Observers
{
get { return Observers; }
}
void ISubject<T>.Attach(IObserver<T> i)
{
Observers.Add(i);
}
IObserver<T> ISubject<T>.Detach(IObserver<T> i)
{
Observers.Remove(i);
return i;
}
void ISubject<T>.Notify()
{
foreach (IObserver<T> o in Observers)
o.Update(SubjectState); //update the observer
}
T ISubject<T>.GetState()
{
return SubjectState;
}
void ISubject<T>.SetState(T state)
{
SubjectState = state;
((ISubject<T>)this).Notify(); //notify the observers of the change
}
}
public interface IObserver<T>
{
void Update(T subjectState);
//T GetState();
}
public class ConcreteObserver<T> : IObserver<T>
{
private T observerState;
private string name;
public ConcreteObserver(string name)
{
this.name = name;
}
void IObserver<T>.Update(T subjectState)
{
observerState = subjectState;
Console.WriteLine(this.name + " is now " + this.observerState);
}
}
Future series of articles on sharepoint:
- sharepoint list -- the concepts of sharepoint list and how to effectively manage it
sharepoint version control -- the internals of sharepoint version control and how to administer and manage the versions
sharepoint permissions -- how to manage the permissions in a large enterprise sharepoint environment
sharepoint server farm -- how to set up a high availability sharepoint server farm
sharepoint document library -- the details on how to get your ways around the document library in sharepoint
sharepoint configuration -- the configurations needed for different sharepoint network scenarios
sharepoint css -- making the most out of customizing sharepoint frontend
sharepoint web services -- some of the most convienent ways to communicate with the internals of sharepoint