Total votes: 1
Category: Design Patterns
Print: Print Article
Please login to rate or to leave a comment.
Published: 22 Aug 2008
Download Sample Code
In this part of the design patterns series we will take a look at the observer design pattern.
The observer pattern is incredibly useful, so useful in fact that it has found itself used all over the place in .NET. Underlying the observer pattern is a simple concept; it’s merely a design to accommodate a one-to-many relationship in a loosely coupled fashion.
Design Patterns Series
Part 1 Learn how to design more robust and maintainable code by incorporating design patterns into your software projects.
Part 2 This part shows the observer design pattern.
Part 3 In this part you will learn about the factory pattern.
Part 4 This part of the design patterns covers the singleton pattern.
Part 5 In this part of the design patterns series we investigate the command pattern and its uses.
Part 6 In this part of the design patterns series we will look at the adapter pattern.
Part 7 In this part of the design patterns series we will take a look at the template pattern.
In this article we will look at the design of the observer pattern. We will implement our own version of the observer pattern, and then we will look briefly at using .NET events to achieve the same result.
What is the observer pattern?
As mentioned in the introduction the observer pattern allows us to implement a one-to-many relationship in a loosely coupled fashion. Loosely coupled means that the objects don’t know much about each other, they know just enough to get the job done.
To illustrate the observer pattern imagine you want to subscribe to Time magazine. You go to the Time magazine website, fill out a few forms and now you are subscribed for a time period of your choosing. In this scenario we will say that Time magazine is the subject, and you, along with all other subscribers are the observers. When a new issue of Time magazine comes out you will be notified via the physical receipt of the latest issue. If we cast our minds back to a time before we were subscribers of the magazine we will remember that when a new issue came out we didn’t receive a copy, and we probably weren’t even told about the availability of a new issue.
In the preceding example scenario we identified that Time magazine was the subject, and we (the people who have paid to receive a copy of the magazine each month) were the subscribers. Unless it is not totally obvious, the subject represents the one in our one-to-many relationship. The subscribers of the magazine are represented by the many.
When the subject has something interesting to inform its observers it will send a notification to each of them. Using our example the notification would be in the form of a copy of the magazine. In the software world the notification may be an object that contains various properties that the subject expects the observers may be interested in. We can apply this methodology to the event arguments often used in .NET. For example when you subscribe to an event on a user interface (UI) control in one of the three UI stacks (WPF, Win Forms, or ASP.NET) the event will often be accompanied with some data specific to that event – data that the observers will probably find very useful. Events aren’t limited to UI controls; you will find events in many types, for example
System.Data.SqlClient.SqlCommand exposes an event
StatementCompleted that is raised when the underlying SQL query has completed executing.
By just thinking about the previous example given we might be leaning towards saying that the subject would contain a list of objects to notify, and you would be correct in doing so. Registering an interest in the subject would simply add that object to the list of observers in the observers list contained within the subject. Similarly unregistering an observer from notifications would simply be a case of removing that observer from the list of observers to notify in the subject.
For our example we will mimic a very common scenario that the crew of the USS Enterprise often found themselves in. Scotty, the infamous engineer down in the engine room will be the subject. Our observers will comprise of the captain (could be Kirk, could be Picard – as an Englishman I am biased towards Picard, or Jean-Luc as he was known to those close to him), and their number one – I’ll go with Riker. To summarize then this is our scenario: we have just been attacked by a hostile Klingon Bird of Prey and we are temporarily limited to our impulse drive, we are relying on good ‘ol Scotty to inform the good captain and Riker when the warp engine is back online. As you can expect the captain and Riker really do need that notification when the warp engine is back online. The design we have is as follows:
Figure 1: Our observer pattern design
As you can see from the previous Figure
Scotty contains zero-or-more IObserver’s. For the purposes of keeping things simple we will use a
List<IObserver> to store all the observers that care about what
Scotty has to say. You will notice that in order to get into the list of observers we need to call the
Subscribe method. This method simply adds an
IObserver to the list of observers in the
Scotty object. We have omitted an
Unsubscribe method to make the sample as concise and as clear as possible.
What happens when
Scotty has something to tell those who are interested in what he has to say? In our simple example we will just traverse each
IObserver in the observers list and call
Update on each of them. Because the
NumberOne differ in the way in which they react to notifications from
Scotty we have used a class for each. All we care about is that the observers implement
We mentioned earlier that often we want to send a bit of information along with the notification. In our example scenario we will send such information as a string (this will suffice to demonstrate the point). To allow this we will modify the
Notify method to take a string parameter.
Figure 2: Adding a way to pass some information to the observers
Implementing our design
Because we have already done all the heavy lifting the implementation is very straightforward. If you have understood the design, you will understand the code. The code listings for each file are as follows:
Listing 1: IObserver.cs
Listing 2: ISubject.cs
Listing 3: Scotty.cs
Listing 4: Captain.cs
Listing 5: NumberOne.cs
Listing 6: Program.cs
One of the main things that we really should talk about is the call to
Program.cs. This is used to synthetically force notifications to the observers, in reality this would be done internally within the subject. It is done in this fashion simply to make the point clearer.
If you run the project you will see the following output via the console.
Figure 3: Result of running the sample program
One thing that we haven’t talked about so far is that of relying upon the order in which notifications are made. In our example observers who are added first will be notified first and so on. You should never code to rely on such occurrences with the observer pattern; doing so would not be loosely coupled.
Aren’t events already in .NET?
Yes. Events in .NET are based upon delegates. Using events in .NET is very simple. Typically you will:
Define a type that represents the data associated with the event
Create a public event member
Create a method responsible for raising that event
For the first point you must derive from
System.EventArgs. For example if we wanted to associate some data with the warp engine coming back online we could do so in a class called
WarpEngineOnlineEventArgs that derived from
System.EventArgs. This event would no doubt carry critical information that each observer would expect to get with each notification.
The second point is very straightforward, all we do is declare a public event member like below:
Why public? The compiler will take care of the rest. Upon compilation the C# compiler will make the member private and generate two public methods:
The add method will register the interest of an observer, the remove method will unregister that interest. When we register a methods interest we do so by using a delegate. The delegate is a pointer to the method to invoke when that event is raised. When the
add_WarpEngineOnline method is called that delegate is added to a list of delegates to invoke when the event is raised. Calling
remove_WarpEngineOnline will remove the correct delegate from the list of delegates to invoke when that event is raised.
For the last point we simply need a method that invokes the event. This is very straightforward; we call the event and then provide the required arguments. As well as providing any additional information with the event in the form of
EventArgs we can also provide information as to the object that raised the event, or rather the sender.
The following code is the same example we used previously but using .NET events.
Listing 7: WarpEngineOnlineEventArgs.cs
Listing 8: Scotty.cs
Listing 9: Captain.cs
Listing 10: NumberOne.cs
Listing 11: Program.cs
If you run the project you will see the same output. Notice that instead of calling the
remove_Xxx methods directly to subscribe to the
WarpEngineOnline event we simply use the
+= operator. This operator has been overloaded to call the
add_Xxx event. Similarly the
-= operator will call the
remove_Xxx method to unsubscribe from the event.
Figure 4: += Operator overloaded
In this part of the design patterns series we have looked at the observer pattern. We have implemented our own primitive version of the observer pattern, as well as using events in .NET. The observer pattern is very useful for modeling one-to-many relationships in a loosely coupled fashion and is used throughout many mainstream frameworks.
The next part of the series will focus on the factory pattern.
Sorry, no bio is available
This author has published 32 articles on DotNetSlackers. View other articles or the complete profile here.
Other articles in this category
Introduction to StructureMap
Have you heard of StructureMap, generally know what it’s for, and want to know how to get started qu...
DI Patterns: Constructor Injection
In this article, an excerpt from the book "Dependency Injection in .NET", we will take a detailed lo...
The Command Pattern
In this article I will provide a quick refresher on what the command pattern is used for, how it wor...
Brian Mains discusses how to implement the Arrange-Act-Assert pattern in TypeMock.
Key Process Patterns
This article, based on chapter 2 of Specification by Example, presents effective patterns for softwa...
Please login to rate or to leave a comment.