C# Design Patterns - Observer Pattern
We’ve covered a couple of design patterns so far in our series. I have been enjoying writing these posts and I hope you have enjoyed reading them or at least got something out of them
In this post we are going to take a look at the Observer pattern.
What Is The Observer Pattern?
The Observer pattern is a way for an object to notify all of it’s dependants when something changes based on a one to many relationship.
The Situation
Continuing to use our city dog registration software senario, one of the project’s requirements is that the dog’s owner’s be notified whenever a dog is processed for an infraction (i.e. picked up by the dog catcher). The Observer pattern is perfectly suited to solve this problem.
Setting Up Our Observer
First before we create our Observer we need to define some classes and interfaces that we will need to use with the Observer.
string Type { get; set; }
int Id { get; set; }
DateTime Date { get; set; }
string Reason { get; set; }
double Fee { get; set; }
}
public class Infraction : IInfraction {
public string Type { get; set; }
int Id { get; set; }
DateTime Date { get; set; }
string Reason { get; set; }
double Fee { get; set; }
}
public interface IOwner {
int Id { get; set; }
string FirstName { get; set; }
string LastName { get; set; }
string EmailAddress { get; set; }
}
public class Owner : IOwner {
public Owner() { }
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string EmailAddress { get; set; }
}
The above code defines an infraction and an owner for our Observer to use. Now we can setup our Observer and the notifications when a dog gets an infraction.
int Id { get; set; }
string Name { get; set; }
void AddInfraction(IInfraction infraction);
void AddOwner(IOwner owner);
}
public class Dog : IDog {
private IList<IOwner> _owners;
private IList<IInfraction> _infractions;
public Dog() {
_owners = new List<Owner>();
}
public void AddOwner(IOwner owner) {
_owners.Add(owner);
}
public void AddInfraction(IInfraction infraction) {
_infractions.Add(infraction);
//notify owners of infraction : the Observer
foreach(IOwner owner in _owners) {
string message = “Dear ” + owner.FirstName + “,\n\n” +
“We are writing you to inform you that there has been an incident with your dog, “ +
this.Name + “, and that the city had to get involved.\n\n” +
“Your dog is being held at the city dog pound for the following reason: “ + infraction.Reason + “.” +
“You can pick them up anytime. There will be a fee of $” + infraction.Fee + ” applied.”;
MessageService.SendEmail(owner.EmailAddress, “cityhall@fictionalcity.com”, message);
}
}
public int Id { get; set; }
public string Name { get; set; }
}
You can see the this pattern at work in the AddInfraction method. Everytime this method is called an infraction is added to the dog’s record and all the owners are sent an email detailing the infraction and where to pick up their dog. It’s nice when you can structure your code to handle situations automatically, for you and your clients.
If you enjoyed this post be sure to grab the RSS feed or follow me on Twitter to get notifications of new posts. Also, check out other posts in this series below.
The C# Design Patterns Series
Part 1 - An Overview
Part 2 - The Decorator Pattern
Part 3 - The Abstract Factory Pattern
Part 4 - The Observer Pattern
RSS ?
11 comments on this post
This might be the normal way of doing it, but in .NET we can use events to do the same thing but easier
That’s true but this is an example of the Observer pattern
Hi,
this is really a nice blog that I often visit.
Yeah, as Steve said same can be done using Events in dotnet and I thing which is a more preferable way. It has mainly two benefits (which you might already be aware) over a classical way.
1. You don’t need to have a collection that would contain list of the observers.
2. And you do not need to iterate over a collection and invoke method.
here is my implementation.
using System;
namespace ObserverPatternTest
{
public delegate void NotifyObserverEventHandler(string Message);
class Program
{
static void Main(string[] args)
{
Bell bell=new Bell();
Student std=new Student();
bell.AddObserver(std);
Teacher teacher=new Teacher();
bell.AddObserver(teacher);
bell.Ring(”This is a First Period”);
bell.RemoveObserver(std);
bell.Ring(”This is a presentation time”);
Console.ReadLine();
}
}
public interface INotifier
{
event NotifyObserverEventHandler NotifyChanged;
void AddObserver(IObserver observer);
void RemoveObserver(IObserver observer);
}
public class Bell : INotifier
{
public void Ring(string Message)
{
if(NotifyChanged!=null)
{
NotifyChanged(Message);
}
}
public event NotifyObserverEventHandler NotifyChanged;
public void AddObserver(IObserver observer)
{
this.NotifyChanged += observer.Update;
}
public void RemoveObserver(IObserver observer)
{
this.NotifyChanged -= observer.Update;
}
}
public interface IObserver
{
void Update(string Message);
}
public class Student : IObserver
{
public void Update(string Message)
{
Console.WriteLine();
Console.WriteLine(”Student Received:”);
Console.WriteLine(Message);
}
}
public class Teacher : IObserver
{
public void Update(string Message)
{
Console.WriteLine();
Console.WriteLine(”Teacher Received:”);
Console.WriteLine(Message);
}
}
}
Great article-series, but wouldn’t it be better to further extract the logic for AddInfraction away from the Dog-model (i know this is not part of the observer-pattern). This way you could leave both the Infraction and the Dog classes to keep only data, and no logic… Just a thought…
I think it would be better to raise an event and pass the infraction as parameter of the event. You can then let each observer decide what to do, either generate a message to email or store parts of the data in other ways. This would further decouple the implementation.
Good Post
This post has good and valuable information, Is nice to see some good articles like this one, thank you.
I love the simplicity of this example. Thanks! Keep up the good work!
Please update the links under The C# Design Patterns Series. I almost stopped at this post not realizing there were more.
Very cool! Thx
2 Trackback(s)