Before downloading or using this product, make sure you understand and accept the terms of the license.
After downloading, make sure to follow the how to use instructions below; they're worth reading.
Please also take a look at running the example as it contains a quick-start example that demonstrates how to use this pattern.
The Observer Template is a set of two C++ template class (tSubject
and tObserver
) that allow you to implement the Observer Pattern using your own custom event classes, on any platform that supports at least the C++98 standard. If you're using a version of C++11 or later, the classes also contain support for the "Move constructor" and "Move assignment operator".
I wrote these classes back in 2012 to scratch an itch for some personal projects; I wanted the ability to send "events" like you might do in ActionScript or what you'd see in Java.awt. I really had "Java" in mind when I was designing this, but I think it ended up looking more like how events look and act in Flash / ActionScript.
I based the design of the two classes directly on the "definition" of the Observer Pattern as detailed in the book Design Patterns: Elements of Reusable Object-Oriented Software by the "Gang of Four". However with that said, there was no code used from the book, and everything for this project was written by me from scratch.
It's taken a little over four years for me to feel like it was ready for a public release. Part of the delay was more or less just "life" and work getting in the way, but I'm glad I was able to finally carve out some time to polish it up for release.
I use these classes consistently throughout my personal projects as well as my client's projects (which is mostly iOS apps nowadays), and in one of those cases an app that's being used by roughly 60,000 people on a daily basis.
The Observer Template is a cornerstone for me, and perhaps you might find it useful too.
If you'd like to take a critical look at how the classes look and work, in the repo above, take a look in the directory called "specimen", which contains a "separated" directory, with class definitions and implementations each in a separate file, or "combined", which contains all class definitions and implementations contained in a single header.
If you find any issues with the source, please let me know. I'm open to contributions to the source, as long as it keeps the classes simple and minimalist. I'm open to add example code to the project for tutorial purposes, particularly if the example can run on multiple platforms.
If you think this page can be cleaned up or made simpler, I'm open to that as well, just contact me with suggestions, and if they're appropriate, I'll consider incorporating them.
Finally, if you use this set of classes in your project– link to this page, and I'll link to your page as well. But you'll have to link to me first
tObserver.h
into your project
#include "tObserver.h"
You'll have the following template classes available for use in your project:
tSubject<T>
- A class you'll inherit from when you want to send events with event class type T
tObserver<T>
- A class you'll inherit from when you want to receive events with event class type T
The tSubject<T>
class does the majority of the "heavy lifting" with regards to the actual observer pattern. The class contains the following public
methods:
::attach(tObserver<T>* observer)
- attaches an observer to this subject. Parameter observer
must be non-null and a unique pointer for the event class type. Parameter observer
will receive event notifications on the next ::notify
call made via this subject.::detach(tObserver<T>* observer)
- detaches an observer from this subject. Parameter observer
must already be attached to the subject for the event class type.::detachAll()
- convenience method, detaches all existing observers for a particular subject. There are no restrictions on this call.::notify(T message)
- notifies any attached observers for the event class type, in the order the observers were added. Subject must not already be notifying for the same event class type or undefined behavior will occur. ::notify
can terminate early if the subject class is destructed during the notification event.
The tObserver<T>
class also does some "heavy lifting", but significantly less of it. It does contain one abstract virtual method ::update
, that you must override in your inheriting classes however. The class contains the following public
methods:
::update(T message)
- Pure virtual method you must implement in your inherited classes. It's up to you what you do in this method. For this method to be called, an observer
must be attach
ed to a subject
, and the subject
's ::notify(T message)
must be called.In both classes, included are a default constructor, copy constructor, and copy assignment operator, which are C++98 compatible, and handle basic initialization and proper copying of the underlying data structures.
For C++11 and later versions, a move constructor and move assignment operator are provided as well.
tSubject<T>
's, and intend to make use of the copy or move constructors / assignment operators, you'll need to ensure that you also provide your own set of copy / move constructors and assignment operators, and ensure that those methods call the underlying tSubject<T>
versions of each.The non-public methods you should never call yourself; basically they just ensure the attachments / detachments update correctly.
Below, I'll provide some basic theory, usage tips, and precautions. Nothing here is really out of the ordinary, but there's a few edge cases you'll likely to run into that you should be aware of.
With these template classes, you can create "subjects" and "observers" to send and receieve:
int
s, double
s, char
s)std::string
, std::vector<std::pair<char, bool> >
, MyEventClass
)double *
, std::string *
, MyEventClass *
)size_t &
, std::string &
, MyEventClass &
)const
references to anything in the first two categories (const MyEventClass &
)
As it turns out, the very last type– const
references– give us the most flexibility with the least cost, if done correctly.
Benefits of const
references:
const
references for event classes allow us to instantiate temporary objects during ::notify
calls::update
calls become "call by reference", which is very light-weight; the object isn't "copied" during the call, and you get the benefits of a "pointer" without having to worry about NULLconst
references mean that the event cannot be modified during a ::notify
by any particular ::update
, which is typically ideal anywayThe last "benefit" might be a drawback if you're looking to modify an event during a notify– however in that case, you can just use non-const references and you're good to go.
With the theory in mind, let's construct a small example to demonstrate a basic use case, in the style I'd recommend.
We'll start with "Event Classes."
"Event classes" are just normal classes. There's actually no "required" structure to these, but I'll provide you with an example of what I typically do.
Since GUIs tend to be good examples of where the observer pattern might shine, let's create an "Event class" that defines a "Window Event." We won't fully flesh out what you might do if you were really making a "Window Event" class, but we'll provide enough variety to show some flexibility.
Here's what we'll include with the class:
WindowEvent
as a generic type nameenum
for the specific event "subtype", to say "what kind" of WindowEvent
are we sendingNULL
just for funOkay, so with that in mind, your WindowEvent class could look like this:
class WindowEvent { public: enum EventType { kOpened = 0, kClosed, kResized, kFocusChanged, kMinimized, }; public: Window* mSource; EventType mEvent; size_t mWidth, mHeight; bool mHasFocus; public: WindowEvent(const EventType& newEventType, Window* newSource = NULL, bool newHasFocus = false) : mSource(newSource), mEvent(newEventType), mWidth(0), mHeight(0), mHasFocus(newHasFocus) { } WindowEvent(const EventType& newEventType, Window* newSource, const size_t& newWidth, const size_t& newHeight) : mSource(newSource), mEvent(newEventType), mWidth(newWidth), mHeight(newHeight), mHasFocus(false) { } };
Constructing WindowEvents as temporary objects will look something like this:
WindowEvent(WindowEvent::kOpened, window); WindowEvent(WindowEvent::kClosed); WindowEvent(WindowEvent::kResized, window, 128, 96); WindowEvent(WindowEvent::kFocusChanged, true);
Of course you can create events like this too:
WindowEvent myEvent(WindowEvent::kResized, window, 128, 96); WindowEvent myOtherEvent = WindowEvent(WindowEvent::kFocusChanged, true);
But we won't need to do it this way because we'll be using const
references / temporary objects.
Now that we have defined an "Event class", let's define a subject to send it.
Since we want to take advantage of using temporary objects during ::notify
, we'll inherit from tSubject<T>
using a const
reference to WindowEvent
as the T
parameter type.
We'll also include a forward declaration to WindowEvent
in case we haven't defined it yet.
class WindowEvent; // Forward Declaration of event class if necessary class Window : public tSubject<const WindowEvent&> { . . // No additional code necessary specific to tSubject<T> . };
…And at a minimum, that's all you need to do!
Now you can ::notify
using the WindowEvent
class, using temporary objects:
Window myWindow; myWindow.notify(WindowEvent(WindowEvent::kOpened, window)); myWindow.notify(WindowEvent::kClosed); myWindow.notify(WindowEvent(WindowEvent::kResized, window, 128, 96)); myWindow.notify(WindowEvent(WindowEvent::kFocusChanged, true));
In the second notify call above, you'll notice I just passed the enum type; this will still work as the compiler knows ::notify
takes WindowEvent
types, and WindowEvent
types have a constructor that supports a single parameter of WindowEvent::EventType
, so a WindowEvent
will be constructed in this case.
So we have a "Subject" called Window
that can send events in the form of an "Event Class" called WindowEvent
(or really a const WindowEvent &
.)
Now we need a class that can listen for these events, (or rather, "Observe" them.)
So we'll make a class called App
, and make it inherit from tObserver<T>
of type const WindowEvent &
. As a reminder, any class can be a tSubject<T>
or tObserver<T>
, and the choice in this case is purely arbitrary and just for example purposes.
Here's the class definition for App
:
class WindowEvent; // Forward Declaration of event class if necessary class App : public tObserver<const WindowEvent&> { . . . virtual void update(const WindowEvent& message); };
As part of the contract in inheriting from tObserver<T>
, we need to provide a definition of ::update(T)
.
In this case we've also defined it as virtual
, in case we want to later inherit from App
and change how we handle WindowEvent
classes, but otherwise virtual
is completely optional and not required.
When you implement the ::update
method of an observer, it's at this point where you actually want to handle the events you have sent out.
Using the WindowEvent
we've defined earlier, we'll handle each event "subtype", that is the enum EventType
we defined earlier, and do different things depending on what events we can handle.
void App::update(const WindowEvent& message) { switch(message.mEvent) { case WindowEvent::kWindowOpened: printf("*** The window opened!\n"); break; case WindowEvent::kWindowClosed: printf("*** The window closed!\n"); break; case WindowEvent::kWindowResized: printf("*** The window resized to: (%lu, %lu)\n", message.mWidth, message.mHeight); break; case WindowEvent::kFocusChanged: printf("*** The window's focused change and now it... %s\n", (message.mHasFocus) ? "has focus" : "lost focus"); break; default: printf("*** %s\n", "Got a window message I couldn't handle!"); break; } }
And with that, we've defined all the classes and methods we need to implement sending and receiving events. All that's left is to wire the subject and observer classes together, and start actually sending events from subjects to observers.
Now that we've fully defined the "Subject class" Window
, and the "Observer class" App
, both are ready to communicate with each other.
So that's just what we'll do– we'll "attach" the "Observer" App
to the "Subject" Window
, via the Subject Window
's ::attach
method:
void someStartupMethod() { App* app = new App(); Window* window = new Window(); window->attach(app); }
And that's all the setup you have to do. Now when you call Window
's ::notify
method, App
will receive ::update
s with whatever events you send.
One important point to note in the code above (and all throughout this example), is that the App
class doesn't have a pointer to Window
and Window
doesn't have a pointer to App
. Now obviously, tSubject<T>
and tObserver<T>
are handling all of this behind the scenes for you, but otherwise the classes know absolutely nothing about each other.
In fact App
and Window
don't need to know anything about each other, no class definitions, includes, or anything like that. All they need to know about is the WindowEvent
type, and any third party can ::attach
them together.
Finally, let's send some events.
Typically, you'll want your Subject class to "own" the responsibility of sending notifications.
Also, since we're not "really" implementing a Window class with nuances like tracking focus, position, size, etc, the method we'll define will just send events as a test– just for demonstration purposes.
With that in mind, we'll define a method in Window
called ::testNotifications
, that will send one of every type of message, just to prove that App::update
will get called with the correct event type.
void Window::testNotifications() { notify(WindowEvent(WindowEvent::kOpened, window)); notify(WindowEvent::kClosed); notify(WindowEvent(WindowEvent::kResized, window, 128, 96)); notify(WindowEvent(WindowEvent::kFocusChanged, true)); notify(WindowEvent(WindowEvent::kFocusChanged, false)); notify(WindowEvent::kMinimized); }
Now, if you were to run the code in this example, you'd see the following output:
*** The window opened! *** The window closed! *** The window resized to: (128, 96) *** The window's focused change and now it... has focus *** The window's focused change and now it... lost focus *** Got a window message I couldn't handle!
That's it– pretty cool, huh? If you were able to follow that, you should be able to implement tSubject
and tObserver
into your own classes and start sending your own events.
Okay– we've gone through an example, everything looks great, and I hope you're inspired so far. However, there's one really crazy scenario that can happen– your Subject class's destructor can be called during a ::notify
call. And it's totally normal, and you should expect it. I think the best way to display this is with a simple visual, so here we go:
Okay, so let's break graphic down:
Window
owns Button
Button
during the constructordelete
s Button
during the destructorButton
sends a ButtonEvent::kButtonPressed
WindowManager::close
then handles by deleting the Window
instance immediatelyButton
instance.
So if you do all of the "math" behind the call stack, it turns out that in this particular case, Button::notify
ends up eventually calling Button::~Button
during Button::sendButtonPressed
!
What this means in this case– and in general– be aware that your class's context (this
) may be gone after a ::notify
completes. So this
, your members, and methods are no longer valid to call or access within this after the ::notify
.
However, there are two things that are still valid to do:
So there's two ways to deal with this potential situation:
::notify
the last thing you call in a methodvoid sendButtonPressed() { . . // Everything important we want to do happens here . notify(ButtonEvent(ButtonEvent::kButtonPressed, this)); // Last thing we do in this method and we're good to go }
This option is fine, and the preferred way if some outside class calls buttonInstance→sendButtonPressed()
, but won't be sufficient if you're calling it via this→sendButtonPressed()
.
So if calling ::notify
last works for you, which it will in a good percentage of cases, you're good to go. Otherwise, there's another alternative.
::notify
completes
This requires a little bit of setup, but the idea here is simple. We keep an extra bool
pointer as a member, keep it NULL until we suspect we're going to "delete ourselves", then set the pointer to a local bool
, and check the result after our "dangerous" ::notify
call.
If we detect we're not destroyed, we "reset" the bool
pointer to NULL, and continue executing code. If not– we exit the method right away as our context is completely gone.
Let's make some changes to the Button
class displayed in the above graphic with the idea that we might want to do more after we call ::notify
, but we also know we might trigger our own demise during that ::notify
:
class Button : tSubject<const ButtonEvent&> { protected: bool* mDeletedPtr; public: Button() : mDeletedPtr(NULL) { . . . } ~Button() { if (mDeletedPtr) { *mDeletedPtr = true; } } . . . void sendButtonPressed() { bool wereWeDeleted = false; mDeletedPtr = &wereWeDeleted; notify(ButtonEvent(ButtonEvent::kButtonPressed, this)); if (!wereWeDeleted) { mDeletedPtr = NULL; . . // Hey, we weren't deleted, we can do more stuff! . } } };
This technique is also the same technique tSubject
uses internally to detect if tSubject
itself gets destroyed during a notify so it knows not to access it's members anymore either.
So the key player here is the mDeletedPtr
member, and basically all we've done is add some boilerplate code to initialize it and set the data it points to.
This solution is more necessary in a language like C++ because in the majority of cases you're doing your own memory management. If this were a garbage collected language like Java– it wouldn't be necessary as the garbage collector would only destroy a class when all "references" to it are gone.
Well, I don't know if this is "advanced" really, but you may or may not be experienced in the following, so here's some tips and tricks.
Literally, everything below are just nuances and / or limitations of C++, so keep in mind these are not my rules, but just the "fringes" of the language.
As you work more and more with tSubject and tObserver, it will likely make sense for a class to send multiple types of Event Classes, or to listen for multiple types of Event Classes.
Don't worry, nothing will "collide" and everything works independently of each other.
To inherit, simply inherit like you normally would:
class MyClass : public tObserver<const FooEvent&>, public tObserver<const BarEvent&>, public tSubject<const ZooEvent&> { . . . void update(const FooEvent& message); void update(const BarEvent& message); };
The above example can ::notify
the ZooEvent
type, and receive the FooEvent
and BarEvent
types.
Just remember to define your ::update
methods before compiling, or your compiler will likely give you "method shadowing" errors.
If you already have a class that defines any type of tObserver inheritance, and you want a child class that handles a different type of tObserver inheritance, it can be done, but you'll also have to re-define whatever your parent's handler is, or else you'll likely get a "method shadowing" error from your compiler.
class RemoteHandler : public tObserver<const URLLoaderEvent&> { virtual void update(const URLLoaderEvent& message); }; class GeoIPHandler : public RemoteHandler, public tObserver<const GeoIPEvent&> { virtual void update(const URLLoaderEvent& message) { RemoteHandler::update(message); // Pass to parent class } void update(const GeoIPEvent&); };
In this case, ensure your parent's ::update
method is virtual
, and at a minimum, you can just pass the parameter to the parent's version of ::update
, and you're good to go. Also, your child class can take the opportunity to do their own handling of the same message the parent does– so there's an upside to the extra code required here.
By far the most interesting side-effect of inheriting from two instantiations of the same template is method naming collisions.
class MyClass : public tSubject<const FooEvent&>, //Assume FooEvent has an std::string constructor public tSubject<const BarEvent&> //Assume BarEvent has an std::string constructor { . . . }; void sendMessages() { MyClass* instance = ...; instance->notify("foo event"); // Error! Which notify do we call?! instance->notify("bar event"); // Error! Which notify do we call?! }
If you've dealt with this before, the solution is quite simple– but ugly– but still simple.
void sendMessages() { MyClass* instance = ...; instance->tSubject<const FooEvent&>::notify("foo event"); // OK! Call the FooEvent constructor and notify instance->tSubject<const BarEvent&>::notify("bar event"); // OK! Call the BarEvent constructor and notify }
Again– this is just how C++ deals with multiple inheritance when naming collisions happen. Not my invention!
If you've built an app, pressure tested it, and ready to release it to the public, I highly suggest you disable assertions; you might see significant speedups. When assertions are on, checks are done during tSubject
's ::attach
and ::detach
methods to ensure observers have proper membership / non-membership in the notification list. With them off, checks aren't made– but if your logic is solid, you won't need the checks anyway.
You do this by either passing -DNDEBUG
as a command-line option to your compiler, or by putting this line at the top of any file that includes tObserver.h
:
#define NDEBUG
The Observer Template comes with two unit test class, tSubjectTests.cc
and tObserverTests.cc
, and three project files:
/project/win/ObserverTemplateTests.sln
- Visual Studio 2005, Windows console target/project/mac/ObserverTemplateTests.xcodeproject
- Xcode 7 Mac OS console target/project/ios/ObserverTemplateTests.xcodeproject
- Xcode 7 iOS console target
When running the unit test on Visual Studio, note you will first need to convert the solution to the latest version. You'll also need to put a breakpoint on the last line of the main
function, as the console window containing the results of the test will close on exit.
When running the unit test on Mac or for iOS, the results of the test will be printed in Xcode's debug console, which is at the bottom right of Xcode's window.
Also, when running for iOS, you'll note that an app won't appear or launch in the simulator or on a device, as the tests are purely meant to display in the debug console.
(If you're curious, these project files were generated with Google GYP; the source project file is located in /project/common/ObserverTemplateTests.gyp
.)
I've included the "WindowEvent" example into the repo, which is based on the sample code from above.
/project/win/WindowEvent.sln
- Visual Studio 2005, Windows console target/project/mac/WindowEvent.xcodeproject
- Xcode 7 Mac OS console target/project/ios/WindowEvent.xcodeproject
- Xcode 7 iOS console targetAs stated in the example code, this is just a light demonstration of the code and does not implement anything related to platform-specific "windowing."
When running the example on Visual Studio, note you will first need to convert the solution to the latest version. You'll also need to put a breakpoint on the last line of the main
function, as the console window containing the results of the test will close on exit.
When running the unit test on Mac or for iOS, the results of the test will be printed in Xcode's debug console, which is at the bottom right of Xcode's window.
Also, when running for iOS, you'll note that an app won't appear or launch in the simulator or on a device, as the tests are purely meant to display in the debug console.
(If you're curious, these project files were generated with Google GYP; the source project file is located in /project/common/WindowEvent.gyp
.)
If you have a question not answered here, please feel free to contact me and ask.
What I'm proposing instead is a method to use the Observer Pattern directly, which requires one header containing two classes, and almost no learning curve.
That said, I've used boost (in general) before on a day-to-day basis for client projects. I'm not a boost "hater", but I'm not a regular user of it either.
Underneath the hood both classes use STL's std::list
type.
Anytime you call either class's constructor or destructor, tSubject
's ::attach
, ::detach
, ::detachAll
, or ::notify
, or tObserver
's ::update
, you're potentially writing (yes, *writing*, in each case) to an std::list
.
As long as you plan your thread-aware code properly with that in mind, you should have no problems.
July 5, 2016
June 28, 2016
April 1, 2012
January 1, 2012