观察者模式(Observer Pattern)解决的对象间的一对多的关系,即一个被观察者与多个观察者。最直接的例子就是公众号(Subject)及订阅者(Observer)。

对于观察者模式、发布-订阅模式/事件监听模式,为只是相同技术实现基础上的不同演化,属于同一类设计模式。


观察者模式1.0

涉及两个角色:

  • 被观察者: Subject/Observable/Publisher, 会有状态/数据变化的对象。
  • 观察者: Observer/Subscriber,关心被观测对象的变化。

最基础的实现需要定义 2 个基类 SubjectObserver

C++实现

观察者

先定义接口类 class Observer,该类没有默认实现,子类必须实现 update() 接口。

1
2
3
4
5
6
// Observer.h 

class Observer {
public:
  virtual void update(string msg) = 0; // abstract virtual
};

被观察对象

基类 Subject,它需要知道有那些观察者,在需要通知的时候遍历观察者列表发送通知。 有自己的实现,定义子类时不需要复写基类的方法,直接使用即可。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// Subject.h

class Subject {
public:
  virtual void addObserver(Observer * observer);
  virtual void removeObserver(Observer * observer);
  virtual void notify(string msg);

private:
  std::set<Observer*> observers;
};
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
// Subjet.cpp

void Subject::addObserver(Observer* observer) {
  observers.insert(observer);
}

void Subject::removeObserver(Observer* observer) {
  observers.erase(observer);
}

void Subject::notify(string msg) {
  for (auto obs: observers) {
    obs->update(msg);
  }
}

应用

以公众号和订阅者为例。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45

// 公众号
class Publisher: public Subject {
public:
    void addArticle(const string& article) {
        articles.push_back(article);
        notify(article);
    }
private:
    vector<string> articles;
};

// 订阅者
class Subscriber: public Observer {
public:
    Subscriber(string name): name(name) {}

    virtual void update(string article) override {
        readArticle(article);
    }

    void readArticle(string article) const {
        cout << name << " is reading article : " << article << endl;
    }

private:
    string name;
};

int main() {

    Publisher publisher;

    Subscriber sub1("Tom");
    publisher.addObserver(&sub1);
    publisher.addArticle("Article 1");

    cout << endl;

    Subscriber sub2("Jerry");
    publisher.addObserver(&sub2);
    publisher.addArticle("Article 2");

    return 0;
}

执行结果:

1
2
3
4
Tom is reading article : Article 1

Tom is reading article : Article 2
Jerry is reading article : Article 2

观察者模式2.0

上面的Observer提供了一个带有一个参数的update接口: virtual void update(string msg)。 虽然在应用中可以通过传递不同参数来区分不同的通知,比如:update("全部订阅者可见更新")update("VIP订阅者可见更新"),但对于复杂一点的需要多参数传递的场景就不适用了。

那么可以通过引入Event类型,以更灵活的定义不同的、复杂的通知类型。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// Event 接口定义

enum EventType {
    ARTICLE_UPDATED,
    VIP_ARTICLE_UPDATED
};

class Event {
public:
    virtual EventType type() = 0;
    virtual ~Event() {}
};

// 具体的 Event 类型
class ArticleUpdatedEvent: public Event {
public:
    virtual EventType type() override { return ARTICLE_UPDATED; }

    string getArticle() const { return article; }
    void setArticle(const string& article) { this->article = article; }
private:
    string article;
};

class VipArticleUpdatedEvent: public Event {
public:
    virtual EventType type() override { return VIP_ARTICLE_UPDATED; }

    string getArticle() const { return article; }
    void setArticle(const string& article) { this->article = article; }

    int getReward() const { return reward; }
    void setReward(int reward) { this->reward = reward; }
private:
    string article;
    int reward;
};

Observer更新为:

1
2
3
4
5
6
7
// Observer.h 

class Observer {
public:
  // virtual void update(string msg) = 0; // abstract virtual
  virtual void update(shared_pointer<Event> event) = 0; // abstract virtual
};

Subject更新为:

1
2
3
4
5
6
7
8
9
// Subject.h

class Subject {
public:
  ...
  // virtual void notify(string msg);
  virtual void notify(shared_pointer<Event> event);
  ...
};

应用到公众号——订阅者上:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
// 公众号

class Publisher: public Subject {
public:
    void addArticle(const string& article) {
        auto e = make_shared<ArticleUpdatedEvent>();
        e->setArticle(article);
        notify(e);
    }
    
    void addVipArticleWithReward(const string& article, int reward) {
        auto e = make_shared<VipArticleUpdatedEvent>();
        e->setArticle(article);
        e->setReward(reward);
        notify(e);
    }
};

// 订阅者
class Subscriber: public Observer {
public:
    Subscriber(string name): name(name) {}

   virtual void update(shared_ptr<Event> event) override {
        switch (event->type()) {
            case EventType::ARTICLE_UPDATED: {
                auto e = dynamic_cast<ArticleUpdatedEvent*>(event.get());
                if (e != nullptr) {
                    string article = e->getArticle();
                    readArticle(article);
                }
                break;
            }
            case EventType::VIP_ARTICLE_UPDATED: {
                auto e = dynamic_cast<VipArticleUpdatedEvent*>(event.get());
                if (e != nullptr) {
                    string vipArticle = e->getArticle();
                    readArticle(vipArticle);
                    int reward = e->getReward();
                    earnReward(reward);
                }
                break;
            }
            default:
                break;
        }
    }

    void readArticle(string article) const {
        cout << name << " is reading article : " << article << endl;
    }

    void earnReward(int reward) const {
        cout << name << " earned reward : " << reward << endl;
    }

private:
    string name;
};

int main() {

    Publisher publisher;

    Subscriber sub1("Tom");
    publisher.addObserver(&sub1);
    publisher.addArticle("Article 1");

    cout << endl;

    Subscriber sub2("Jerry");
    publisher.addObserver(&sub2);
    publisher.addVipArticleWithReward("Article 2", 5);

    return 0;
}

执行结果:

Tom is reading article : Article 1

Tom is reading article : Article 2
Tom earned reward : 5
Jerry is reading article : Article 2
Jerry earned reward : 5

通过继承Event,这个版本支持灵活的扩展通知类型,对传递的参数个数和类型没有任何限制。

目前为止,被订阅对象发送 notify 时对所有的订阅者都是一视同仁的,也就是说每个订阅者接收到的消息是一样的。

对于上面公众号的例子,如果想实现对 VIP 订阅者和普通订阅者推送不同的通知,那就可以通过如下的方式。


观察者模式3.0

更改 Subject 的订阅和通知方法:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

// Subject.h

class Subject {
public:
  virtual void addObserver(EventType type, Observer * observer);
  virtual void removeObserver(EventType type, Observer * observer);
  virtual void notify(shared_ptr<Event> event);

private:
  std::map<EventType, std::set<Observer*> > observers;
};

// Subjet.cpp

void Subject::addObserver(EventType type, Observer* observer) {
  observers[type].insert(observer);
}

void Subject::removeObserver(EventType type, Observer* observer) {
  observers[type].erase(observer);
}

void Subject::notify(shared_ptr<Event> event) {
  for (auto obs: observers[event->type()]) {
    obs->update(event);
  }
}

修改main

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
int main() {
    Publisher publisher;

    Subscriber sub1("Tom");
    publisher.addObserver(EventType::ARTICLE_UPDATED, &sub1);
    publisher.addArticle("Article 1");

    cout << endl;

    Subscriber sub2("Jerry");
    publisher.addObserver(EventType::VIP_ARTICLE_UPDATED, &sub2);
    publisher.addVipArticleWithReward("VIP Article 2", 5);

    return 0;
}

执行结果:

Tom is reading article : Article 1

Jerry is reading article : VIP Article 2
Jerry earned reward : 5

此版本进化到了一个更为特殊的观察者模式———发布订阅者模式,或者叫事件订阅模式。

Observer Pattern v.s. Publish-Subscribe Pattern

其主要区别是观察者模式涉及两种角色 SubjectObserver,而发布订阅模式中间多了一个Event处理,有的称之为BrokerChannel:

Observer Pattern v.s. Publish-Subscribe Pattern


完整代码