Chris's coding blog

C# Design Patterns: the Observer pattern

February 28, 2009

Summary

Very similar to the Mediator pattern. The only difference is the ‘clients’, or the Observers as they are now called, don’t broadcast via the mediator (now called the Subject). The observers just sit and listen for event(s) being broadcast by the Subject. The subject can be doing anything it likes completely independent of the Observers; the observers as the name implies just observe, they aren’t expected to interact with the subject in the pattern.

Example

System messages is one example of the Observer pattern - where lots of Observers sit listening for various different messages. In Java ActionListeners implement a form of the Observer pattern (and Command pattern), in C# the delegate callback system for events makes the Observer pattern transparent with the syntax of the language. You can find examples of this with events in control libraries.

namespace DesignPatterns
{
/// <summary>
/// The use of an interface here mightseem a bit redundent, but it's to keep
/// inline with the original gang of 4 definition of the pattern. The implementing class
/// is still responsible for wiring itself to the Subject's notify event.
/// </summary>
public interface IObserver
{
void Update(string message);
}
public class Observer : IObserver
{
/// <summary>
/// A friendly name for the Observer.
/// </summary>
public string Name { get; set; }
/// <summary>
/// Filters messages starting with the string provided.
/// </summary>
public string MessageFilter { get; set; }
/// <summary>
/// Creates a new instance of an Observer.
/// </summary>
/// <param name="subject">The class that broadcasts/notifies us of its actions.</param>
/// <param name="name">A friendly name for the observer.</param>
public Observer(Subject subject, string name)
{
Name = name;
subject.Notify += new Action<string>(Update);
}
/// <summary>
/// Called each time the Subject notifies this Observer.
/// </summary>
/// <param name="message"></param>
private void Update(string message)
{
// Filter the message?
if (!string.IsNullOrEmpty(MessageFilter))
{
if (message.StartsWith(MessageFilter))
Console.WriteLine("{0} received the message {1}", Name, message);
}
else
{
Console.WriteLine("{0} received the message {1}", Name, message);
}
}
}
/// <summary>
/// This is the class that would perform all or any tasks and then inform
/// each subscribed Observer when it has down this.
/// </summary>
public class Subject
{
/// <summary>
/// Tradionalists might want this to use a delegate called *EventHandler
/// but I'm being lazy and using the Generic Action<T> delegate.
/// </summary>
public event Action<string> Notify;
public void Run()
{
Thread thread = new Thread(new ThreadStart(InternalRun));
thread.Start();
}
/// <summary>
/// A small mockup to simulate doing some work.
/// </summary>
private void InternalRun()
{
string message;
// 5 basic messages, sleeping between them to simulate
// intensive IO operations or calculations.
message = "Calculating Pi to 2000 dp.";
OnNotify(message);
Thread.Sleep(2000);
message = "Copying an item over the network using Vista pre-SP1";
OnNotify(message);
Thread.Sleep(2000);
message = "Finding all files that end in *.dll";
OnNotify(message);
Thread.Sleep(2000);
message = "Formatting a floppy.";
OnNotify(message);
Thread.Sleep(2000);
message = "Spidering google.";
OnNotify(message);
Thread.Sleep(2000);
}
/// <summary>
/// Send out a notification to all Observers.
/// </summary>
protected virtual void OnNotify(string message)
{
if (Notify != null)
Notify(message);
}
}
}
view raw gistfile1.cs hosted with ❤ by GitHub

csharpdesign-patterns

Chris Small

I'm Chris Small, a software engineer living and working in London. This is my tech blog. Find out more via GithubStackoverflowResume