I am/we are using the observer pattern for many things in our application. I think it's time to create a reusable base class for these situations. Most implementations I could find only do something like this:
class Subject { public: void AttachObserver(Observer* observer) { observers.push_back(observer); } ... };
I would prefer to be able to attach a callback function instead of a class. I came up with this:
namespace Observe
{
using ObserverID = uint64_t;
template<typename... callback_arg_types>
class Obserable
{
public:
using CallbackFunction = std::function<void(callback_arg_types...)>;
public:
ObserverID AttachObserver(CallbackFunction callback)
{
std::unique_lock<std::mutex> block(_mutex);
const auto id = GetNewID();
_observers.emplace(id, std::move(callback));
return(id);
}
bool DetachObserver(ObserverID& id)
{
std::unique_lock<std::mutex> block(_mutex);
const auto it = _observers.find(id);
if (it == _observers.end())
{
return(false);
}
_observers.erase(it);
id = ObserverID();
return(true);
}
protected:
void NotifyAllObservers(callback_arg_types... args)
{
std::unique_lock<std::mutex> block(_mutex);
for (const auto& observer : _observers)
{
observer.second(args...);
}
}
private:
ObserverID GetNewID()
{
static ObserverID id = 0;
return(++id);
}
std::mutex _mutex;
std::unordered_map<ObserverID, CallbackFunction> _observers;
};
}
Usage:
using namespace Observe;
class IntPublisher : public Obserable<int>
{
public:
void Publish(int number)
{
NotifyAllObservers(number);
}
};
IntPublisher publisher;
ObserverID id = publisher.AttachObserver([](int number)->void{
std::cout << number << std::endl;
});
publisher.Publish(42);
publisher.DetachObserver(id);
I don't like the ObserverID. I know I will never run out of ids, but I think there must be a more elegant solution to get a reference/handle to the attached observer/callback function (so that it can be detached later).
I would appreciate comments, corrections, criticism and suggestions.