Introduction to the Observer Pattern

Introduction to the Observer Pattern

Welcome back! We're continuing our exploration of Behavioral Patterns in C++. In this lesson, we will delve into the Observer Pattern, another fundamental pattern that emphasizes object communication and responsibility distribution. This pattern allows an object, known as the subject, to maintain a list of its dependents, called observers, and notify them automatically of any state changes, usually by calling one of their methods.

Previously, we looked at the Command Pattern, which encapsulates a request as an object. Now, let's build on that knowledge and explore how the Observer Pattern facilitates communication between objects in a seamless and efficient manner.

What You'll Learn

In this lesson, you will learn how to implement the Observer Pattern by understanding its main components and their roles. We'll break down the pattern into manageable parts and demonstrate its practical use through clear examples.

Here's a simple illustration to get you started:

We start with defining a NewsPublisher class that maintains a list of subscribers. When new news is published, the NewsPublisher notifies all subscribers by calling their update method. Each Subscriber can then process the news as needed.

class NewsPublisher {
public:
    void addSubscriber(Subscriber* subscriber) {
        subscribers.push_back(subscriber);
    }

    void removeSubscriber(Subscriber* subscriber) {
        subscribers.erase(std::remove(subscribers.begin(), subscribers.end(), subscriber), subscribers.end());
    }

    void publish(const std::string& news) {
        for (auto subscriber : subscribers) {
            subscriber->update(news);
        }
    }

private:
    std::vector<Subscriber*> subscribers;
};

Now, let's define the Subscriber interface and a concrete implementation ConcreteSubscriber that prints the received news to the console.

class Subscriber {
public:
    virtual void update(const std::string& news) = 0;
    virtual ~Subscriber() {}
};

class ConcreteSubscriber : public Subscriber {
public:
    ConcreteSubscriber(const std::string& name) : name(name) {}

    void update(const std::string& news) override {
        std::cout << name << " received news: " << news << std::endl;
    }

private:
    std::string name;
};

Finally, we demonstrate the Observer Pattern in action by creating a NewsPublisher, adding subscribers, publishing news, and removing a subscriber.

int main() {
    NewsPublisher* newsPublisher = new NewsPublisher();
    Subscriber* subscriber1 = new ConcreteSubscriber("Subscriber 1");
    Subscriber* subscriber2 = new ConcreteSubscriber("Subscriber 2");

    newsPublisher->addSubscriber(subscriber1);
    newsPublisher->addSubscriber(subscriber2);

    newsPublisher->publish("Breaking News 1");
    newsPublisher->removeSubscriber(subscriber1);
    newsPublisher->publish("Breaking News 2");

    delete newsPublisher;
    delete subscriber1;
    delete subscriber2;
    return 0;
}

Let's understand the key components of the Observer Pattern in this example:

  • Subject (NewsPublisher): This class maintains a list of observers and notifies them of any state changes. In this case, the NewsPublisher class has a list of Subscriber objects and notifies them when new news is published.
  • Observer (Subscriber): This class defines an interface for receiving updates from the subject. In this example, the Subscriber class has a pure virtual function update that is implemented by concrete subscribers.
  • Concrete Observer (ConcreteSubscriber): This class implements the update method to receive and process updates from the subject. In this example, the ConcreteSubscriber class prints the received news to the console.
  • Client (main function): This part of the code demonstrates how to create a NewsPublisher, add subscribers, publish news, and remove subscribers.
Sign up

Join the 1M+ learners on CodeSignal

Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal