|
|
Comments and Discussions
|
|
 |

|
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: }
|
|
|
|

|
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 !!!
|
|
|
|

|
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.
|
|
|
|

|
<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.
|
|
|
|

|
Thanks very much. this was very useful to me
|
|
|
|

|
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?
|
|
|
|

|
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 !!!
|
|
|
|

|
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)?
|
|
|
|

|
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 !!!
|
|
|
|

|
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
|
|
|
|

|
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.
Is there any better way to implement this in C++? Does C++ provide any mechanism to detect the dangling pointers?
ABCDEFG...
|
|
|
|

|
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 !!!
|
|
|
|

|
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
|
|
|
|
|

|
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.
|
|
|
|

|
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 !!!
|
|
|
|

|
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.
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!
|
|
|
|
|

|
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 !!!
|
|
|
|
|

|
template <class T>
class Subject
{
typedef Observer<T>::observer_type;
void attach (observer_type &observer);
};
class PanicSirene : public Subject<Temperature>::observer_type
{
};
|
|
|
|

|
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 News Suggestion Question Bug Answer Joke Rant Admin
Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.
|
Using C++ Templates to overcome some of the original Subject/Observer design pattern problems
| Type | Article |
| Licence | |
| First Posted | 28 Nov 2002 |
| Views | 108,473 |
| Bookmarked | 55 times |
|
|