Implementing a functor-based observer pattern in C++

When developing software, it is often necessary to implement a communication pattern where one or more objects are notified when a particular event occurs in another object. The Observer pattern provides a solution to this problem by allowing objects (observers) to subscribe to events in a subject object and receive notifications when those events occur.

In C++, we can implement the Observer pattern using functors, which are objects that can be called like functions. Functors provide a flexible way to encapsulate the observer logic and allow for customization behavior.

The Subject Class

Let’s start by implementing the subject class, which is the object that generates events and notifies the observers.

class Subject {
public:
    using ObserverFn = std::function<void()>;

    void subscribe(const ObserverFn& observer) {
        observers.push_back(observer);
    }

    void notify() {
        for (const auto& observer : observers) {
            observer();
        }
    }
private:
    std::vector<ObserverFn> observers;
};

In the code above, we define a Subject class that contains a vector of ObserverFn, which is the type alias for our functor-based observer. The subscribe function allows observers to subscribe to events, and the notify function iterates over all observers and calls them.

The Observer Class

Now, let’s create an example of an observer class that will handle the events generated by the subject.

class Observer {
public:
    explicit Observer(const std::string& name) : name_(name) {}

    void operator()() const {
        std::cout << "Observer " << name_ << " received notification\n";
    }
private:
    std::string name_;
};

In this example, we define an Observer class with a constructor that takes a name as a parameter. The operator() function is implemented, which allows the object to be called like a function. When the observer is called, it simply prints a notification message.

Putting it all together

Now, let’s put everything together and see how we can use the observer pattern implemented with functors.

int main() {
    Subject subject;

    Observer observer1("Observer 1");
    Observer observer2("Observer 2");

    subject.subscribe(observer1);
    subject.subscribe(observer2);

    std::cout << "Notifying observers...\n";
    subject.notify();

    return 0;
}

In the main function, we create a Subject object and two Observer objects. We subscribe both observers to the subject using the subscribe function. Finally, we call subject.notify() to trigger the notification process.

When we run the above code, it will output:

Notifying observers...
Observer Observer 1 received notification
Observer Observer 2 received notification

As you can see, both observers receive the notification from the subject.

Conclusion

By implementing the functor-based observer pattern in C++, we can easily decouple the subject and observer objects, allowing for a flexible and customizable event notification mechanism. Functors provide a powerful and expressive way to define the observer logic, making our code more modular and maintainable.

So, next time you need to implement an observer pattern in C++, consider using functors for a clean and efficient solution.

#C++ #ObserverPattern #Functors