📖
Go C++
  • Introduction
  • Chapter 1: What You Must Know First
    • Virtual Address Space of Process: Memory Partition and Layout
    • Function Call: Stack Frame
    • Program Compiling and Linking
  • Chapter 2: C++ Basics Improvement
    • Default Parameters
    • Inline Function
    • Function Overloading
    • new and delete
    • const and Pointers
    • References in Detail
  • Chapter 3: Object-Oriented Principles
  • Class and Object
  • Constructor and Destructor
  • Shallow Copy and Deep Copy
  • Initializer List
  • Various Member Functions
  • Pointer to Class Members
  • Chapter 4: Template Programming
  • Function Templates
  • Class Templates
  • Memory Allocators
  • Chapter 5: Operator Overloading
    • Operator Overloading
    • Introduction to Iterators
    • Issues of Iterator Invalidation
    • More about new and delete
    • Overloading of new and delete: Object Pool
  • Chapter 6: Inheritance and Polymorphism
    • Look inside Inheritance
    • More about Inheritance
    • Virtual Functions, Static Binding and Dynamic Binding
    • More about Virtual Functions
    • Understanding Polymorphism
    • Abstract Classes
    • Frequently Asked Interview Questions: Polymorphism
  • Chapter 7: Multiple Inheritance
    • Virtual Inheritance and Virtual Base Classes
    • Diamond Problem
    • Four Kinds of Type Conversions
  • Chapter 8: Standard Template Library
    • Sequence Containers
    • Container Adaptors
    • Associative Containers
    • More about Iterators
    • Function Objects
    • Generic Algorithms, Binders and Lambda Expressions
  • Chapter 9: Object Optimization
    • Behind the Object
    • Optimizing Objects in Functions
    • Member Functions with Rvalue References
    • Move Semantics and Perfect Forwarding
  • Chapter 10: Smart Pointers
    • Smart Pointers
    • Smart Pointers without Reference Counting
    • Smart Pointers with Reference Counting
    • Custom Deleters
  • Chapter 11: Function Objects and Binders
    • More about Binders
    • Introduction to std::function
    • Template Specialization and Argument Deduction
    • More about std::function
    • std::bind(): A Simple Thread Pool
    • More about Lambda Expressions
  • Chapter 12: Multithreading
    • Important Features in C++11
    • Multithreaded Programming with std::thread
    • Mutual Exclusion
    • Producer-Consumer Problem
    • Atomic Operations
    • Thread Visibility and volatile
  • Chapter 13: Design Patterns
    • Singleton Pattern
    • Factory Pattern
    • Proxy Pattern
    • Decorator Pattern
    • Adapter Pattern
    • Observer Pattern
Powered by GitBook
On this page

Was this helpful?

  1. Chapter 13: Design Patterns

Observer Pattern

The observer pattern belongs to the behavior pattern, which focuses on the communication between objects. The observer pattern applies to a one-to-many relationship of objects, where multiple objects rely on a single object. When the state of the object changes, other objects can receive corresponding notifications.

A common observer pattern is the publish-subscribe model, where the observers subscribe to a subject, and the subject publishes notification to the observers when its status changes.

Here we have an abstract class Observer, and three classes that inherit from it. These classes override the handle() method which handles messages according to their IDs. Observer1 handles message 1 and 2, while Observer2 handles message 2 and 3, and Observer3 handles message 1 and 3.

class Observer {
public:
    virtual void handle(int id) = 0;
};
​
class Observer1 : public Observer {
public:
    void handle(int id) {
        switch (id) {
            case 1:
                cout << "Message 1" << endl;
                break;
            case 2:
                cout << "Message 2" << endl;
                break;
            default:
                cout << "Unknown message" << endl;
                break;
        }
    }
};
​
class Observer2 : public Observer {
public:
    void handle(int id) {
        switch (id) {
            case 2:
                cout << "Message 2" << endl;
                break;
            case 3:
                cout << "Message 3" << endl;
                break;
            default:
                cout << "Unknown message" << endl;
                break;
        }
    }
};
​
class Observer3 : public Observer {
public:
    void handle(int id) {
        switch (id) {
            case 1:
                cout << "Message 1" << endl;
                break;
            case 3:
                cout << "Message 3" << endl;
                break;
            default:
                cout << "Unknown message" << endl;
                break;
        }
    }
};

Then we have the Subject class which keeps an unordered map. The map keys are message IDs, and the values are lists of observers. The class has two methods subscribe() and publish(). The former takes an Observer and the message ID to be subscribed, and add the observer to the map with message ID as the key. publish() takes the message ID to be published, takes out the observers from the map who have subscribed to this message, and call their handle() method one by one.

class Subject {
public:
    void subscribe(Observer *observer, int id) {
        _map[id].push_back(observer);
    }
    void publish(int id) {
       auto it = _map.find(id);
       if (it != _map.end()) {
           for (Observer *p : it->second) {
               p->handle(id);
           }
       }
    }
private:
    unordered_map<int, list<Observer *>> _map;
};

Now in the main() function, we subscribes the observers to the messages they would like to handle. When the status of the subject changes, it notify the observers by publishing the corresponding messages.

int main() {
  Subject subject;
  Observer *p1 = new Observer1();
  Observer *p2 = new Observer2();
  Observer *p3 = new Observer3();
​
  subject.subscribe(p1, 1);
  subject.subscribe(p1, 2);
  subject.subscribe(p2, 2);
  subject.subscribe(p2, 3);
  subject.subscribe(p3, 1);
  subject.subscribe(p3, 3);
​
  subject.publish(1);
  cout << "--------------------" << endl;
  subject.publish(2);
  cout << "--------------------" << endl;
  subject.publish(3);
  cout << "--------------------" << endl;
  subject.publish(4);
  delete p1, p2, p3;
}

The above code has the following outputs:

Observer1: Message 1
Observer3: Message 1
--------------------
Observer1: Message 2
Observer2: Message 2
--------------------
Observer2: Message 3
Observer3: Message 3
--------------------
PreviousAdapter Pattern

Last updated 4 years ago

Was this helpful?