当一个对象的状态发生改变时,你如何通知依赖于它的其他对象?

解决方案

观察者模式:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时, 所有依赖于它的对象都得到通知并被自动更新。每个观察者都可以根据目标的变化分别采取各自的操作。

观察者模式是一种有效描述对象间相互作用的模式。

适用性

当一个对象改变时需要通知或者改变其他对象情形

结构

Observer.png

模式的组成

观察者模式包含如下角色:
目标(Subject):目标需要记录它的观察者,观察者可以有多个,提供注册,删除和通知观察者对象的接口。通知接口通过调用Observer的udapte接口,告知它们自身状态已经改变。
具体目标(ConcreteSubject):维护自身状态,在自身状态改变时(setState),调用notify接口通知观察者。
观察者(Observer):定义一个更新接口,供Subject调用。
具体观察者(ConcreteObserver):维护一个指向ConcreteSubject对象的引用,存储有关状态,通过更新接口使这些状态应与目标的状态保持一致。

效果

观察者模式允许你独立的改变目标和观察者。你可以单独复用目标对象而无需同时复用其观察者,反之亦然。还可以在不改动目标和其他的观察者的前提下增加观察者。

下面是观察者模式其它一些优点:

  1. 观察者模式可以实现表示层和数据逻辑层的分离,并定义了稳定的消息更新传递机制,抽象了更新接口,使得可以有各种各样不同的表示层作为具体观察者角色。
  2. 在观察目标和观察者之间建立一个抽象的耦合:一个目标所知道的仅仅是它有一系列观察者 , 每个都符合抽象的Observer类的简单接口,但是不知一个观察者属于哪一个具体的类。这样目标和观察者之间的耦合是抽象的和最小的。
    因为目标和观察者不是紧密耦合的,它们可以属于一个系统中的不同抽象层次。一个处于较低层次的目标对象可与一个处于较高层次的观察者通信并通知它,这样就保持了系统层次的完整。如果目标和观察者混在一块,那么得到的对象要么横贯两个层次 (违反了层次性),要么必须放在这两层的某一层中(这可能会损害层次抽象)。
  3. 支持广播通信:目标发送的通知不需指定它的接收者,通知被自动广播给所有已向该目标对象登记的有关对象。处理还是忽略一个通知取决于观察者。
  4. 观察者模式符合“开闭原则”的要求。

观察者模式的缺点

  1. 如果一个被观察者有很多直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。
  2. 如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。
  3. 观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。
  4. 意外的更新。在目标上一个看似无害的的操作可能会引起观察者以及依赖于这些观察者的那些对象的更新。此外,如果依赖准则的定义或维护不当,常常会引起错误的更新,这种错误通常很难捕捉。

实现

#include <iostream>
#include <vector>
#include <string>
using namespace std;

// 观察者
class JobStation
{
public:
    virtual void update(){}
};

//目标,被观察者
class Subject
{
public:
    virtual void attach(JobStation* observer){}
    virtual void detach(JobStation* observer){}
    virtual void notify(){}
};

// 顾客,被观察者
class Customer: public Subject
{
public:
    explicit Customer(string state)
    {
        this->state = state;
    }

    void attach(JobStation* observer)
    {
        observers.push_back(observer);
    }

    void detach(JobStation* observer)
    {
        vector<JobStation *>::iterator it;
        for(it=observers.end(); it!=observers.end(); it++)
        {
            if (observer == *it)
                observers.erase(it);
        }
    }

    void notify()
    {
        for(int i=0; i<observers.size(); i++)
        {
            observers[i]->update();
        }
    }

    string getState()
    {
        return state;
    }

    void setState(string state)
    {
        this->state = state;
        notify();
    }

private:
    vector<JobStation *> observers;
    string state;
};

//会计,观察者
class Accountant: public JobStation
{
public:
    explicit Accountant(Customer* customer)
    {
        this->customer = customer;
    }

    void update()
    {
        if (customer->getState() == "已付款")
        {
            cout << "我是会计,我来开具发票。" << endl;
            accountState = "已开发票";
        }
    }
private:
    Customer* customer;
    string accountState;
};

//出纳,观察者
class Cashier: public JobStation
{
public:
    explicit Cashier(Customer* customer)
    {
        this->customer = customer;
    }

    void update()
    {
        if (customer->getState() == "已付款")
        {
            cout << "我是出纳员,我给登记入账。" << endl;
            cashierState = "已入帐";
        }
    }
private:
    Customer* customer;
    string cashierState;
};

// 快递员,观察者
class Deliveryman: public JobStation
{
public:
    explicit Deliveryman(Customer* customer)
    {
        this->customer = customer;
    }

    void update()
    {
        if (customer->getState() == "已付款")
        {
            cout << "我是配送员,我来发货。" << endl;
            deliveryState = "已发货";
        }
    }
private:
    Customer* customer;
    string deliveryState;
};

int main()
{
    Customer *customer = new Customer("未付款");
    customer->attach(new Accountant(customer));
    customer->attach(new Cashier(customer));
    customer->attach(new Deliveryman(customer));

    customer->setState("已付款");
    return 0;
}

总结与分析

将一个系统分割成一系列相互协作的类有一个常见的副作用:需要维护相关对象间的一致性。通过观察者模式,把一对多对象之间的通知依赖关系的变得更为松散,降低了各类之间的耦合性,大大地提高了程序的可维护性和可扩展性,也很好的符合了开放-封闭原则。

参考链接