Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

In a C++ code base I've been working on, they have a bunch of instances of the observer pattern, but it's a little different from the classical pattern. In the classic, the Observer is a specific interface with specific method(s) that are called when the Observable changes or has new data.

Observable                       Observer
register( Observer* )            update( data1, data2 ... )

In our code base, we use a template class that takes the number and type of arguments to the update() method as parameters, and allows you to specify any pointer-to-method as the recipient of the data.

Observable                          
register( Functor<data1, data2> )   

Is this an improvement over the original pattern? Here's what I think are the pros of each approach:

Classic:

  • clarity - the interface marks which classes are recipients of observable data
  • less code - the boiler plate and ugly template syntax to create the functor are somewhat onerous

Variant:

  • flexibility - I can make any object in my system receive the update (if it has the right method signature) without having to change the original object

As a another option, do I have it all wrong that this is the Observer pattern in practice?

share|improve this question
1  
many time the observer needs some state, (which is why in the old C implementations for callbacks a void* is passed along for the first arg) –  ratchet freak Feb 19 '13 at 13:28

3 Answers 3

up vote 5 down vote accepted

Your idea was already invented 10 years ago by Herb Sutter. See this article: http://www.drdobbs.com/cpp/generalizing-observer/184403873, it contains a full explanation.

So the answer is

  • yes, it is an improvement over the original GOF variant of the pattern

  • no, it is not better than every other known implementation of the Observer pattern

Also note that what you suggested here is the "standard observer implementation" in functional languages or languages with functional elements where events/event sinks are typically implemented in a comparable way (for example, in C# using delegates).

share|improve this answer
    
A lot more than ten years ago by Herb Sutter. It's just the OOP way of taking a function pointer, which was in qsort in C and I'm sure we can find callbacks used a long time before that, too. –  DeadMG Feb 19 '13 at 13:44
    
@DeadMG: the OP was asking specificially about the observer pattern using functors in C++. That's exactly what the article is about. Of course, the core ideas are older, but lets stick to the question. –  Doc Brown Feb 19 '13 at 13:48

Just to check, you do know that your Functor is just std::function, right?

Secondly, it absolutely is better. You can register lambdas, function objects, etc, so it's far more flexible than requiring derivation, and you don't have to define ten billion interfaces, all of which are dupes of each other.

share|improve this answer
    
Unfortunately we aren't able to use C++ 11 on this project. The actual implementation involves N different classes for functions that take up to N arguments. Blech. –  Sesquipedalian Feb 19 '13 at 13:49
    
You can't "disconnect" with std::function so I would argue that it depends on if you need disconnection or not. If you don't then it is far better in simple cases. –  Klaim Feb 19 '13 at 14:12
    
@Ses: function is in Boost and TR1 as well. –  DeadMG Feb 19 '13 at 19:41
    
@Klaim: Just return a key object to the user (for example, an iterator into a linked list where the function is). I've done this myself. –  DeadMG Feb 19 '13 at 19:42

I implemented a lot of variants of Observer pattern in C++ and one thing I learnt is that there is no generic way to do it unfortunately, a bit like trying to make a unique compiler.

It depends on what feature you need for the specific usage. One important feature that is or not required is disconnection.

Disconnecting an observer requires that we can identify the observer to disconnect. There are several ways to do this. Boost.Signals(2) do this by providing an object that is basically a handle to the connection and can be used to disconnect.

Your first example allow disconnection, as we can identify an observer by comparing pointers; your second example don't allow disconnection if Functor is std::function<> as it can't be compared (there is explicitly no == operator).

If your Functor<> can be compared correctly, then it is possible to disconnect. If it's not, then depending on the use case, it can be or not a problem.

Also, note that an Observer class can be more helpful in defining a set of messages that Must be all handled by the observer.

Really, it depends on the use case.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.