Click here to Skip to main content
Click here to Skip to main content

Implementing a Subject/Observer pattern with templates

By , 10 Mar 2003
 

Introduction

The traditional Subject/Observer pattern as described in the famous Design Patterns book has one annoying disadvantage: the observer does not get a handle to the notifying subject in its update method. This means that:
  • The observer needs to store a pointer to the subject to which it is attached.
  • If the observer is attached to multiple objects, it has no way of determining which one of them is notifying him.
This article tries to solve these problems by making use of C++ templates.

The original design pattern

The original design pattern makes use of two base classes: Subject and Observer. The Subject base class contains all the logic of storing all the attached observers. The Observer class just contains a pure virtual method (update()), that needs to be filled in by the inheriting observer class. Please read the 'Design Patterns' book for all details. The update() method of the Observer class does not get any parameters, which means that a class that inherits from Observer does not know where the notification came from. It is not difficult to add a 'Subject' parameter to the update method, but since the real subject inherited from the 'Subject' base class, the observing class always needs to perform a down-cast, which could be dangerous.

The solution

Instead of defining two base classes, we will define two template clases. Both template classes will be based on the subject-class that is able to notify other classes (the observers).
template <class T>
class Observer
   {
   public:
      Observer() {}
      virtual ~Observer() {}
      virtual void update(T *subject)= 0;
   };
The first enhancement here is that our pure virtual update method gets a pointer to the subject as argument; not the base Subject class (which is shown hereafter), but the class that was given as parameter to the template definition.
template <class T>
class Subject
   {
   public:
      Subject() {}
      virtual ~Subject() {}
      void attach (Observer<T> &observer)
         {
         m_observers.push_back(&observer);
         }
      void notify ()
         {
         std::vector<Observer<T> *>::iterator it;
         for (it=m_observers.begin();it!=m_observers.end();it++) 
              (*it)->update(static_cast<T *>(this));
         }
   private:
      std::vector<Observer<T> *> m_observers;
   };
Here, we defined the basic Subject class/template. The attach method simply adds the observer (which is of the basic Observer<T> class) to a vector. The notify method simply notifies all observers. Both templates can be used in any situation where the Subject/Observer pattern can be used. The following classes describe how they are used.
class Temperature : public Subject<Temperature>
   {
   public:
      Temperature() {}
      ~Temperature() {}
      void temperatureChanged () {notify();}
      void getTemperature() {std::cout << 
         "   Getting the temperature." << std::endl;}
   };
Our Temperature class is a class that monitors the temperature, and notifies its observers when the temperature changes. As you can see all it has to do is call the notify() method. The getTemperature method simply writes something on the screen, but of course in real-life situations it should return the actual temperature. Taking a look at the implementation of the notify() method. It simply calls the update() method of all attached observers, but with itself as argument. Since 'this' (which is the Subject class) is cast to the type T, the update() method of the observer will get the correct argument type, as shown in the following example:
class PanicSirene : public Observer<Temperature>
   {
   public:
      PanicSirene() {}
      ~PanicSirene() {}
      void update (Temperature *subject)
         {
         std::cout << "Temperature was changed, sound the sirene" 
               << std::endl;
         subject->getTemperature();
         }
   };
As you can see, a pointer to the Temperature instance that trigger the notification is given as argument to the update method. The observing class (PanicSirene in this case) can simply call any method of the notifying subject, in this case simply getTemperature. The following source shows how it is effectively used:
Temperature       temp;
PanicSirene       panic;

temp.attach (panic);

temp.temperatureChanged ();
The following output will be generated:
Temperature was changed, sound the sirene
   Getting the temperature.

Observing multiple subjects of a different type

The templates are still easy to use if you need to attach the observer to multiple objects. Suppose that we have a similar subject-class for measuring the pressure.
class Pressure : public Subject<PRESSURE>
   {
   public:
      Pressure() {}
      ~Pressure() {}
      void pressureChanged () {notify();}
      void getPressure() {std::cout << "   Getting the pressure." 
          << std::endl;}
   };
If we want to show both the temperature and pressure in a window that shows all environment-related information, we simply create our EnvironmentWindow like this:
class EnvironmentWindow : public Observer<Temperature>, 
        public Observer<Pressure>
   {
   public:
      EnvironmentWindow() {}
      ~EnvironmentWindow() {}
      void update (Temperature *subject) {std::cout << 
             "Temperature was changed" << 
             std::endl; subject->getTemperature();}
      void update (Pressure    *subject) {std::cout << 
             "Pressure was changed"    << 
             std::endl; subject->getPressure   ();}
   };
The class simply inherits twice from the Observer template, for both the Temperature and for the Pressure. Notice that we have two update methods here, one for the temperature, one for the pressure. The following example shows how it can be used:
Temperature       temp;
Pressure          press;
EnvironmentWindow win;
PanicSirene       panic;

temp.attach (win  );
temp .attach (panic);
press.attach (win  );

temp.temperatureChanged ();
press.pressureChanged ();
And it shows the following output:
Temperature was changed
   Getting the temperature.
Temperature was changed, sound the sirene
   Getting the temperature.
Pressure was changed
   Getting the pressure.

Observing multiple subjects of the same type

If our PanicSirene class needs to verify both the internal temperature and the external temperature, we don't need to modify anything to our implementation of the PanicSirene class. We simply attach our class instance to both Temperature classes.
Temperature       internalTemp;
Temperature       externalTemp;
PanicSirene       panic;

internalTemp.attach (panic);
externalTemp.attach (panic);

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

Patje
Software Developer
Belgium Belgium
Member

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
Generalg++ main.cc:40:error expected ';' before "it"memberSimon Asselbergs18 May '05 - 14:31 
Very useful article. I Downloaded the code and tried to compile it... It doesn't seem to compile in gcc (g++), my compiler says: main.cc:40:error expected ';' before "it"... Anyone?
 
38: void notify ()
39: {
40: std::vector<Observer<T> *>::iterator it;
41: for (it=m_observers.begin();it!=m_observers.end();it++) (*it)->update(static_cast<T *>(this));
42: }
 

GeneralRe: g++ main.cc:40:error expected ';' before &quot;it&quot;memberPatje18 May '05 - 20:39 
Thanks you liked the article.
 
I checked the sources again and recompiled them and they compile correctly.
I am using Visual Studio .NET 2003 (version 7.1 of the compiler), but at the time I wrote this article I used Visual C/C++ 6.0 so it should compile with that version as well.
Maybe a problem with the G++ compiler?
 
Did you try to compile the sources directly after downloading it or did you make some changes?
I see something different on line 40. The code that you show with the error is on line 25 in the original code.
 
Patje.
 
Enjoy life, this is not a rehearsal !!!


GeneralRe: g++ main.cc:40:error expected ';' before "it"memberSimon Asselbergs19 May '05 - 4:12 
The article is really great since I am doing some research on Mixin Classes, MVP and Subject Observer and generic programming issues in general. I had some code already in screen but commented it with //, this explains why the line number was slightly different. Ofcourse this should not be the reason for compiling errors. Then I copy-pasted your code fresh in main.cpp and it still didn't compile for the same reason. Then I broke it down in more header files with one diver file. The Observer header compiled ok when I included it into the driver file.
 
I compiled it using the -Wall flag and haven't tried to compile it with the -pedantic option (this to keep the warnings at peace). But maybe I have to compile it with -pedantic to exclude the possibillity of non ANSI code, but again I am puzzled because the code looks as it has to compile. I also compiled a simpler template vector::iterator it; And this compiled ok. Macro like features can sometimes make debugging a little bit harder to follow....
 
I compiled it on my Gentoo Linux box (a linux system totaly compiled from scratch) so it should be able to compile if it is ANSI code. As soon as I have it compiled I can code my first C++ version of my MVP Framework I developed in other languages. If you or anyone else has any hint for me to try, please don't hesitate. That would be really cool. In the mean time I will try to isolate the problem.. Thanks for your reply.
GeneralRe: g++ main.cc:40:error expected ';' before &quot;it&quot;sussAnonymous1 Aug '05 - 18:28 
<small><b>Simon Asselbergs wrote:</b></small>
<i>Very useful article. I Downloaded the code and tried to compile it... It doesn't seem to compile in gcc (g++), my compiler says: main.cc:40:error expected ';' before "it"... Anyone?
 
38: void notify ()
39: {
40: std::vector<Observer<T> *>::iterator it;
41: for (it=m_observers.begin();it!=m_observers.end();it++) (*it)->update(static_cast<T *>(this));
42: }
</i>
 

 
Was having the same problem. This might help.
 
The line:
 
std::vector<Observer<T> *>::iterator it;
 
...is considered ambiguous, as iterator could be a type or a non-type (eg: a function).
 
Somewhere in the Subject class, use the line:
 
typedef typename std::vector<Observer<T> *>::iterator observerIterator;
 

then replace line 40 with...
 

observerIterator it;
 

..or..
 
just replace line 40 with...
 

typename std::vector<Observer<T> *>::iterator it;
 

While I've found it compile and seems to work, I'm not exactly sure why. I'm not very famililiar with the notion of incomplete types and using the typename keyword. Why don't we get the same error when doing std::vector<int>::iterator it? It seems as if only when the type of vector is a template itself is this considered ambiguous.
GeneralRe: g++ main.cc:40:error expected ';' before &quot;it&quot;sussAnonymous1 Sep '05 - 4:04 
Thanks very much. this was very useful to me
GeneralSTL container of instances issues & other limitationsmembereohrnberger28 Jan '05 - 9:42 
The implementation of observer pointers in the subject class is a little bit concerning to me. If observers are collected in an STL container as full instance copies, then anytime the STL container of Observer instances adjusts it memory, or moves the Observer instances around, the Subject's pointers to those Observers are invalidated and will cause an access violation when they are used.
 
Should the Observer have a back pointer to its Subject (would appear to be the next logical development), and the Subjects are contained in STL containers as instances, when the Subjects move around in the STL container, the Observer's pointer to its Subject are invalidated and will cause an access violation when they are used.
 
Further analysis would seem to lead to the conclusion that Subject copy and Subject assignment operations of Observer pointers is transitive in nature, i.e. the target Subject of the copy or assignment operation end up with the Observer pointer list. This makes the STL containers of Subject instances work quite naturally as the STL containers move their Subject contents around inside of them. Of course with the Observation transition from one Observer to another, the source Observer needs to be disconnected from it's Subject, and the target Observer needs to be connected to the Subject.
 
Observer copy and assignment operations are not transitive. It's duplicative, meaning that when you copy or assign an Observer you end up with two Observers observing the same subject. Care must be taken to make sure that the Observer copy and Observer assignment operations attach the target Observer with the source Observer's subject. On Observer destruction, a call to each of it's Subjects Deleting( Observer* ) method needs to be made, so that the Subject can remove the Observer from it's list of Observers. On Subject destruction, it needs to first detach itself from all of its Observers.
 
The tricky part in all of this is who gets to maintain the Observer's back pointer to it's Subject, especially if the Observer can observe multiple Subjects? I elected to make the Subject always maintain their Observer's back pointers as data is the boss, and the Subject represents the data.
 
The notion that an observer can observe only and single instance of a subject is limiting. A more flexible implementation would be that an observer can observe any number of subjects. Passing a base class Subject pointer to the Observer::Update( Subject* ) method, it can be determined using RTTI and dynamic casts what the real Subject type is, and the Observer can then, based on which type the subject is, make the appropriate method calls to update it's rendering of the Subject (granted this means that in the Update method you'll need a case / switch or a number of 'if ... else if ...' statements. The final 'else' in the 'if / else' sequence coughed out an error message, or could throw an exception, that an unrecognized Subject was encountered, and you knew that the Observer established an observing relationship with something that it didn't deal with when it should have. I'd not view the multiple 'if / else if's as all that bad, as typically an Observer is only observing very few different types of subjects.
 
Another alternative is making the Subject and Observer templates, and declaring them as base classes with the template argument being the class being Observed or Subjected. i.e. class AppObserver : public Observer< AppSubject > and class AppSubject : public Subject< AppSubject >. Since the Observers Update and Deleting methods are pure virtual, it forces application subclasses to implement the needed methods. Another advantage is that there is no need for RTTI, as part of the templated method signature determines which method is called.
 
I like this design much better, but it relies much more heavily on templates, which may or may not be in the capabilities of following C++ code maintainers.
 
When doing things in this manner, full object instances in the STL containers and making sure that the copy CTOR and assignment operators are correct, it is very important that you really understand the difference between object instance identity and object instance equivalence. I in fact had to implement a base class that did nothing more than provide a static counter which was incremented each time a subclass instance is created and stored on CTOR as it's identity value. This same base class provided the ID method so that you knew that you were comparing identity rather than equivalence. With a simple predicate class comparing the identity value of subclasses, it was easy to get the STL containers of object instances to be searchable based on this criterion.
 
This leads to the need of providing a DeepCopy method for times when the observation relationships were not copied like in the copy CTOR and assignment operations. This is specifically required when the application really wants a duplicate of an object instance hierarchy which has a completely separate identity.
 
The only question I have is this design going to bite me in the butt later?

GeneralRe: STL container of instances issues &amp; other limitationsmemberPatje31 Jan '05 - 0:24 
In most situations, both subject and observer classes will be sufficiently big enough to have them in a memory location that is not easily moved around in memory. I think you should make a distinction between:
- simple values (numbers, strings, dates, ...) that are small enough to be put in a container (like a vector), without performance problems if they are moved around.
- data structures (order information, customer information, ...) that is too large to be put in an STL container by itself.
You see a similar evolution in languages like C#, which make a distinction between values (which can be put on the stack), and a class instance (which can only be new()'d).
 
Making sure that all pointers are valid is important of course, but was beyond the topic of this article. I only wanted to describe a construction that solved one of the subject/observer pattern problems.
The consistency problem is found in many more locations and isn't typically for this topic.
 
Notifications from the observer to the subjects that it is going out of scope can easily be added (but aren't strictly necessary here).
 
If an observer is deleted, then it should remove itself from the subjects it watches.
 
Thanks for your comment.
 
Enjoy life, this is not a rehearsal !!!


GeneralGood place to start from but...sussELemings24 Oct '03 - 23:29 

There are few issues that would have to be addressed to make your implementation more generic.
 
1. What if one or more observers are destroyed? In your implementation, there is no way for the subject to "release" an observer. (This is brought up in item 4 on page 297 of the GoF's book.) Similarly, there is no way for an observer to simply "detach" itself from the subject.
 
2. What if I wanted to use the associative lookup strategy discussed at the top of page 297 in the GoF's book?
 
3. What if I didn't need a parameter in Observer's update() function at all? What if I wanted to pass a reference to an entirely different "event" class (containing more detailed information about the subject change for example)?
GeneralRe: Good place to start from but...memberPatje28 Nov '03 - 22:25 
You are right, but I never intended the code to be complete. I just started using C++ and templates when I wrote the article so the article was more about investigating the possibilities of templates and finding out some tricks, rather than delivering a full, complete class that simply needs to be plugged in in your application.
Unfortunately I don't have much spare time, otherwise I would rewrite the article and add all the missing parts.
 
Thanks for your input.
 
Enjoy life, this is not a rehearsal !!!


GeneralNotification on a Observer vector copy and other stuffmemberMyttO22 Apr '03 - 5:53 
Interesting article.
 
I wrote my own Observation pattern using STL and have been running it for years.
 
Here is a couple of enhancements experience incited me to write I think you
may have in yours:
  • Add a Detach(Observer<T> &observer) method
    • Make a copy of the m_observers vector, and run updates on it: as an update to a notification, it may be possible for the observer to detach (views in particular).
  • Clean all observations up when destroying both subjects and observers, it prevents you from updating a dead guy.
  •  
  • Try to take into account cyclic notifications, and avoid which are often unregular though possibly correct: may be have a bool m_bNotifyMutex you should set to true at the begining of notify and false at the end. At least warn, or -- better -- through exception when a cyclic notification occurs.
  •  
  • use a bool m_bInhibited and escorting get/set to prevent the subject from notifying in certain circumstences. It might boost performance up, typically when subject batches a lot of notifying modification to its state.

 
-MyttO
Software Architect
www.graisoft.com
GeneralRe: Notification on a Observer vector copy and other stuffsuss-Dan-13 Jul '04 - 14:25 
I got problem of "dead observer" too. But to clean up the observations when destroying both subjects and observers, I have to keep a reference to the subject in the observer side (using C++). As a result, there is a cyclic link between subject and observer. (observer has to know subject, while subject has to know a bit about observer.) This is ugly. Confused | :confused:
Is there any better way to implement this in C++? Does C++ provide any mechanism to detect the dangling pointers?
 
ABCDEFG...
GeneralRe: Notification on a Observer vector copy and other stuffmemberPatje14 Jul '04 - 21:18 
Recently I got in this problem as well.
I got a document, that needed to know all of its views; and each view needed to know about the document. At first, I couldn't decide on who managed the pointers to the other.
I was afraid that I had to make the view class a friend of the document class and vice versa.
 
The solution I came up with was to introduce a third class, the relation class.
I gave the document class private methods to attach and detach a view.
I gave the view class private methods to set and clear the document pointer.
Then I made the relations class friend of both other classes, and added attach and detach methods to the relations class.
The relations::attach method calls both document::attach and view::setdocument to achieve the link between them.
The relations::detach method calls both document::detach and view::cleardocument to break the link between them.
Now the document and view only needs to call the relations class in their destructor.
 
Hope this helps.
 
Enjoy life, this is not a rehearsal !!!


GeneralNice job!memberAlvaro Mendez10 Apr '03 - 11:14 
This is well worth a 5. Nice job!
 
Regards,
Alvaro
 

When birds fly in the right formation, they need only exert half the effort. Even in nature, teamwork results in collective laziness. -- despair.com
GeneralRe: Nice job!memberPatje10 Apr '03 - 21:19 
Thanks Alvaro.
Although I'm already programming almost 20 years, I've only picked up real C++ the last year. Historical reasons in the company environment have limited our development to C so far.
 
I also like the more pure C++ articles here on CodeProject, rather than the fancy MFC, .Net, C# or ASP articles your find here mostly. Although those articles could be useful, I more enjoy writing pure C++ articles, because they are useful for all C++ developers. If you like this article and its use of templates, you might enjoy my other 2 articles too.
 
Thank you for your support.
 
Enjoy life, this is not a rehearsal !!!
 
My Articles:
- Implementing a Subject/Observer pattern with templates
- Different ways of writing class factories
- AutoRunner: a template class to automatically run start- and cleanup-code in code blocks
Generalnice idea, problematic implementationsussAnonymous10 Dec '02 - 18:33 
When you use this kind of machnism, the observer class needs to "know" about the subject, and this matter breaks the whole idea of encapsulation.
That's why they call it Observer. I needs to observe, without really knowing any class declerations of any subjects.
What if I want to write a new module (dll) to my software solution, which I don't want it to link and know the dll that holds the implementation class of the subject it should observe ?
 
I the case the observer should preform different actions based on the subject, then downcasting is still better - as long as you know and sure of what you are doing.

GeneralRe: nice idea, problematic implementationmemberPatje10 Dec '02 - 21:44 
I think that the situations in which you don't know what you are observing are limited.
After all, the observer still needs to attach itself to the subject class, so why is it forbidden to make use of it afterwards.
The whole idea of the observer/subject mechanism is to be able to add more observers, without modifying the code of the observing class. Not the other way around.
 
If you need to observe something you don't wanna know about, you're probably observing the wrong class. Suppose that in my example the temperature can be modified by 5 different devices, each implemented by a class. It would be wrong to observe each of these 5 classes in my observer. There are two alternatives to observe it the right way:
  • Let all 5 classes inherit from the same base class and observe that base class. In this case the base-class will use the template class while the 5 inheriting class are simply sub-classes (without the use of templates).
  • Let all 5 classes notify the temperature manager, and let the observer observe the temperature class.
 
Regarding your questions about putting part in a DLL: because the observer needs to attach itself to the subject, it needs to have the class definition of a subject (not necessarily the one I described).
If the observer does not want to do anything with the subject, you can indeed use the original subject/observer pattern.
If the observer needs information of the subject, it still needs the class definition of a subject that has the useful methods and for that you could use one of the alternatives I described above.
 

 
Enjoy life, this is not a rehearsal !!!

GeneralRe: nice idea, problematic implementationmemberChrisHowe9 Jan '03 - 1:13 
In the cases where you don't need to know anything about the subject, and dont want to for reasons of encapsulation, just use the original subject-observer pattern. I dont see any reason why you can't have both, and use them selectively, depending on how much you need to know about the subject. Thats exactly what I intend to do after writing this.Smile | :)
 
In cases where the Update is just used as a kind of object-oriented event mechanism I'll use the old pattern. In cases where the Update is needs to actually make use of the subject, I can use the templated version.
 
Note that if you use abstract base classes as interfaces to improve encapsulation, then the observer will only probably need to know about an interface, rather than the concrete class that is calling it, which shouldn't really damage your encapsulation.
 
I think this is great, good job!
 

GeneralNicely presented, well writtenmemberPaul Evans8 Dec '02 - 1:24 
Hi there,
 
I've only recently got my head around the template inheritance thing myself, and this seems like such a good use of the techinque to implement the Observer pattern.
 
I recently posted my own article (http://www.codeproject.com/useritems/PDESingletonTemplateNoMFC.asp) which uses templates in this manor, with its own sinmple Observer class to manage singletons. I found that the templates seem to have wierd rules when inheriting from them about what can access the protected mutators. Perhaps the protected relationship is not transative, so it only goes down the inheritence tree - as a base class does not seem to be able to access protected members of classes inheriting from it. I guess that makes some kind of sense! WTF | :WTF:
 
It's always nice to see people using the STL rather then the MFC for things too, when the MFC is not necessary.
 
Good work! Smile | :)

 
//////////////////////////////
// Paul Evans, UK.
//////////////////////////////

GeneralRe: Nicely presented, well writtenmemberPatje8 Dec '02 - 21:13 
Thanks for your reaction Paul.
After years of writing plain C (because of portability and historical reasons), I just started writing C++ some months ago. I decided to do it right from the first time, and I think understanding all the aspects of STL is important.
Regarding MFC, I have some problems.
Although I never used it (so far), MFC seems to be a good way of quickly writing applications. Also, I noticed that lot of the articles on the CodeProject use MFC. Plugging in some MFC controls I find here seems tempting, but I'm a bit afraid of the future of MFC. Isn't .Net a better platform for the future?
And what about other platforms? Currently we're only running on Windows, but what if we want support Linux in two or three years?
It's probably difficult to answer on that.
 
Thanks anyway for your reactions, and I'm going to read your article now.
 
Enjoy life, this is not a rehearsal !!!

GeneralRe: Nicely presented, well writtenmemberPaul Evans8 Dec '02 - 22:25 
Mutual admiration going on here, which is cool Smile | :)
 
The MFC is good for RAD, but it's a bit messy and ties you to MS. The STL is more platform independent, and actually properly uses templates etc, and tends on the whole to be less bloated. However it's one of those arguments that could run and run and run Smile | :)

 
//////////////////////////////
// Paul Evans, UK.
//////////////////////////////

GeneralTypedef Subject's Observermemberjwchiu4 Dec '02 - 15:08 
template <class T>
class Subject
{
  typedef Observer<T>::observer_type;
  void attach (observer_type &observer);
};
 
class PanicSirene : public Subject<Temperature>::observer_type
{
};
 


GeneralRe: Typedef Subject's ObservermemberPatje4 Dec '02 - 20:43 
Thanks for your tip jwchiu (how do you pronounce that? or is that an abbreviation?).
It is similar to a tip of one of my colleagues, who suggested to put the definition of the Observer class in the Subject class.
I write down this suggestion and incorporate it in the next version. Thanks.
 
Enjoy life, this is not a rehearsal !!!

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web04 | 2.6.130523.1 | Last Updated 11 Mar 2003
Article Copyright 2002 by Patje
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid