定义

   定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。

   Observer模式描述了如何建立这种关系。这一模式中的关键对象是目标(subject)和观察者(observer)。一个目标可以有任意数目的依赖它的观察者。一旦目标的状态发生改变,所有的观察者都得到通知。这种交互也称为发布-订阅(publish-subscribe)。目标是通知的发布者。故观察者模式又名 依赖(Dependents),发布-订阅(Publish-Subscribe)。

 适用场景

 以下任何一种情况可以使用观察者模式:

  • 当一个抽象模型有两个方面,其中一个方面依赖于另一个方面。将这两者封装在独立的对象中以使它们可以各自独立地改变和复用。

  • 当对一个对象的改变需要同时改变其它对象,而不知道具体有多少对象有待改变。

  • 当一个对象必须通知其它对象,而它又不能假定其它对象是谁。换言之,你不希望这些对象是紧密耦合的。

代码示例

   观察者模式,首先需要定义观察者基类(接口)Observer和目标基类(接口)Subject,观察者基类需要对外提供一个接口函数Update,Update是一个纯虚函数,方便多态的实现,供目标通知观察者更新。

移动开发培训,Android培训,安卓培训,手机开发培训,手机维修培训,手机软件培训

class Subject;class Observer{    public:        virtual ~Observer();        virtual void Update(Subject* theChangedSubject) = 0;    protected:
        Observer();
};

移动开发培训,Android培训,安卓培训,手机开发培训,手机维修培训,手机软件培训

   目标基类需要提供支持观察者订阅函数Attach;观察者取消订阅函数Detach;对已订阅的所有观察者进行广播的函数Notify,在目标变化后,该函数遍历通知所有已订阅的观察者;以及用于存储已订阅该目标的所有观察者的列表结构List

移动开发培训,Android培训,安卓培训,手机开发培训,手机维修培训,手机软件培训

class Subject{    public:        virtual ~Subject();        virtual void Attach(Observer *);        virtual void Detach(Observer *);        virtual void Notify();    protected:
        Subject();    private:
        List<Observer *> *_observers;
};void Subject::Attach(Observer* o){
    _observers->Append(o);
}void Subject::Detach(Observer *o){
    _observers->Remove(o);
}void Subject::Notify(){
    ListIterator<Observer*> i(_observers);    for(i.First(); !i.IsDone(); i.Next()){
        i.CurrentItem->Update(this);
    }
}

移动开发培训,Android培训,安卓培训,手机开发培训,手机维修培训,手机软件培训

     基于目标接口类Subject和观察者接口类Observer,下面是四人帮写的设计模式一书中的观察者模式应用——一个简单的时钟,每当定时器滴答一下,定时器(目标)就会通知时钟(观察者)绘制界面更新显示。

  ClockTimer是基于目标接口类实现的目标实例,GetHour、GetMinute、GetSecond分别用于获取时、分、秒,Tick函数为计时器实现函数,每过一个滴答时刻,该函数就会调用目标基类的Notify函数通知所有已订阅观察者对象。

移动开发培训,Android培训,安卓培训,手机开发培训,手机维修培训,手机软件培训

class ClockTimer::public Subject{public:
    ClockTimer();    virtual int GetHour();    virtual int GetMinute();    virtual int GetSecond();    void Tick();
};void ClockTimer::Tick(){    //update internal time-keeping state    //...    Notify();
}

移动开发培训,Android培训,安卓培训,手机开发培训,手机维修培训,手机软件培训

   DigitalClock继承Widget和Observer,实现观察者实例。构造函数中初始化目标实现类ClockTimer,并订阅当前观察者对象;析构函数中取消订阅当前观察者对象;Update函数是观察者基类纯虚函数的真正实现,当目标ClockTimer更新,调用Notify时,会调用到当前对象Update函数,在Update函数中再调用Draw更新时钟界面显示。

移动开发培训,Android培训,安卓培训,手机开发培训,手机维修培训,手机软件培训

class DigitalClock::public Widget,public Observer{public:
    DigitalClock(ClockTimer *);    virtual ~DigitalClock();    virtual void Update(Subject *);    //overrides Observer operation

    virtual void Draw();    //overrides Widget operation;    //defines how to draw the digital clockprivate:
    ClockTimer* _subject;
};

DigitalClock::DigitalClock(ClockTimer *s){
    _subject = s;
    _subject->Attach(this);
}

DigitalClock::~DigitalClock(){
    _subject->Detach(this);
}void DigitalClock::Update(Subject * theChangedSubject){    if(theChangedSubject == _subject){
        Draw();
    }
}void DigitalClock::Draw(){    //get the new values from the subject

    int hour = _subject->GetHour();    int minute = _subject->GetMinute();    //etc.    //draw the digital clock}

移动开发培训,Android培训,安卓培训,手机开发培训,手机维修培训,手机软件培训

   有了上述观察者和目标的实现类后,就能实现基于观察者设计模式的数字时钟实现,code为:

ClockTimer *timer = new ClockTimer;//新建目标对象DigitalClock *digitalClock = new DigitalClock(timer);//新建观察者对象并订阅ClockTimer目标对象

  上例是目标-观察者一 一对应的示例,比如实现多个相同时间的数字时钟,这时会用到单目标-多观察者模型:

移动开发培训,Android培训,安卓培训,手机开发培训,手机维修培训,手机软件培训

ClockTimer *timer = new ClockTimer;
DigitalClock *digitalClock1 = new DigitalClock(timer);//Observer 1DigitalClock *digitalClock2 = new DigitalClock(timer);//Observer 2.
.
.
DigitalClock *digitalClockn = new DigitalClock(timer);//Observer n

移动开发培训,Android培训,安卓培训,手机开发培训,手机维修培训,手机软件培训

    当要实现监听多个端口的TCP连接的程序,这时会用到多目标-单观察者模型,需要在上ClockTimer和DigitalClock的基础上稍作修改,将观察者类中的目标用二维指针来存储目标对象,目标类、观察者类以及应用代码如下: 

  目标类:

移动开发培训,Android培训,安卓培训,手机开发培训,手机维修培训,手机软件培训

class ClientTCPConnect::public Subject{public:
    TcpConnect();    virtual void OnReceivedData();//Received Data

    virtual void GetData();
};void ClientTCPConnect::OnReceivedData(){    //...    Notify();
}void ClientTCPConnect::GetData(){    //Get TCP Data}

移动开发培训,Android培训,安卓培训,手机开发培训,手机维修培训,手机软件培训

   观察者类:

     ServerTCPConnect构造函数中传入目标者类的指针和大小,并订阅所有目标者类。

移动开发培训,Android培训,安卓培训,手机开发培训,手机维修培训,手机软件培训

class ServerTCPConnect::public Observer{public:
    ServerTCPConnect(ClientTCPConnect **, int size);    virtual ~ServerTCPConnect();    virtual void Update(Subject *);    //overrides Observer operation

    virtual void DealClientConnect(Subject *);private:
    ClientTCPConnect **_sbuject;    int _size;

};

ServerTCPConnect::ServerTCPConnect(ClientTCPConnect**s, int size){
    _subject = s;    for(int i = 0; i < _size; i++){
        _subject[i]->Attach(this);
    }
}

ServerTCPConnect::~ServerTCPConnect(){    for(int i = 0; i < _size; i++){
        _subject[i]->Detach(this);
    }
}void ServerTCPConnect::Update(Subject * theChangedSubject){    for(int i = 0; i < _size; i++){        if(theChangedSubject == _subject[i]){
            DealClientConnect(_subject[i]);
        }
    }
}void ServerTCPConnect::DealClientConnect(Subject *s){
    ClientTCPConnect *tcp = (ClientTCPConnect *)s;
    tcp->GetData();//deal with TCP Data}

移动开发培训,Android培训,安卓培训,手机开发培训,手机维修培训,手机软件培训

   应用:

移动开发培训,Android培训,安卓培训,手机开发培训,手机维修培训,手机软件培训

int n=10;ClientTCPConnect **ClientConnect = new ClientTCPConnect*[n];for(int i = 0; i < n; i++){
    ClientConnect[i] = new ClientTCPConnect;
}
ServerTCPConnect *serverConnect = new ServerTCPConnect(ClientConnect, n);

移动开发培训,Android培训,安卓培训,手机开发培训,手机维修培训,手机软件培训

   再复杂的就是多目标-多观察者模型,可由上多目标-单观察者和单目标-多观察者模型组合而成。 

移动开发培训,Android培训,安卓培训,手机开发培训,手机维修培训,手机软件培训

int n = 10;
ClientTCPConnect **ClientConnect = new ClientTCPConnect*[n];for(int i = 0; i < n; i++){
    ClientConnect[i] = new ClientTCPConnect;
}int m = 20;
ServerTCPConnect **ServerConnect = new ServerTCPConnect*[m];for(int j = 0; i < m; j++){
    ServerConnect[i] = new ServerTCPConnect(ClientConnect, n);
}

移动开发培训,Android培训,安卓培训,手机开发培训,手机维修培训,手机软件培训

   上面基本上都是相同的观察者和目标类,那么目标和观察者为不同的类时,该如何组合成多目标——多观察者的实例呢?

   目标类ClientTCPConnect,ClientTCPConnect1,、、、,ClientTCPConnect_n的实现:

移动开发培训,Android培训,安卓培训,手机开发培训,手机维修培训,手机软件培训

class ClientTCPConnect::public Subject{public:
    ClientTCPConnect();    virtual void OnReceivedData();//Received Data

    virtual void GetData();
};void ClientTCPConnect::OnReceivedData(){    //...    Notify();
}void ClientTCPConnect::GetData(){    //Get TCP Data}
.
.
.class ClientTCPConnect_n::public Subject{public:
    ClientTCPConnect_n();    virtual void OnReceivedData_n();//Received Data

    virtual void GetData_n();
};void ClientTCPConnect_n::OnReceivedData_n(){    //...    Notify();
}void ClientTCPConnect_n::GetData_n(){    //Get TCP Data}

移动开发培训,Android培训,安卓培训,手机开发培训,手机维修培训,手机软件培训

   观察者类ServerTCPConnect,ServerTCPConnect1,、、、,ServerTCPConnect_m的实现:

移动开发培训,Android培训,安卓培训,手机开发培训,手机维修培训,手机软件培训

class ServerTCPConnect::public Observer{public:
    ServerTCPConnect(Subject **, int size);    virtual ~ServerTCPConnect();    virtual void Update(Subject *);    //overrides Observer operation

    virtual void DealClientConnect(Subject *);private:
    Subject **_subject;
    ClientTCPConnect_k *_subject_k;
    .
    .
    .
    ClientTCPConnect_l *_subject_l;    int _size;

};
ServerTCPConnect::ServerTCPConnect(Subject **s, int size){
    _subject = s;
    _subject_k = (ClientTCPConnect_k*)_subject[k];
    _subject_k->Attach(this);
    .
    .
    .
    _subject_l = (ClientTCPConnect_l*)_subject[l];
    _subject_l->Attach(this);
}

ServerTCPConnect::~ServerTCPConnect(){
    _subject_k->Detach(this);
    .
    .
    .
    _subject_l->Detach(this);    
}void ServerTCPConnect::Update(Subject * theChangedSubject){    for(int i = 0; i < _size; i++){        if(theChangedSubject == _subject[i]){
            DealClientConnect(_subject[i]);
        }
    }
}void ServerTCPConnect::DealClientConnect(Subject *s){    if(ClientTCPConnect *tcp = dynamic_cast<ClientTCPConnect*>(s)){
        tcp->GetData();//deal with TCP Data        ...
    }
    .
    .
    .    else if(ClientTCPConnect_n *tcp = dynamic_cast<ClientTCPConnect_n*>(s)){
        tcp->GetData_n();//deal with TCP Data        ...
    }
}
.
.
.class ServerTCPConnect_m::public Observer{public:
    ServerTCPConnect_m(Subject **, int size);    virtual ~ServerTCPConnect_m();    virtual void Update(Subject *);    //overrides Observer operation

    virtual void DealClientConnect_m(Subject *);private:
    Subject **_subject;
    ClientTCPConnect_k *_subject_k;
    .
    .
    .
    ClientTCPConnect_l *_subject_l;    int _size;
};

ServerTCPConnect_m::ServerTCPConnect_m(Subject **s, int size){
    _subject = s;
    _subject_k = (ClientTCPConnect_k*)_subject[k];
    _subject_k->Attach(this);
    .
    .
    .
    _subject_l = (ClientTCPConnect_l*)_subject[l];
    _subject_l->Attach(this);
}

ServerTCPConnect_m::~ServerTCPConnect_m(){
    _subject_k->Detach(this);
    .
    .
    .
    _subject_l->Detach(this);    
}void ServerTCPConnect_m::Update(Subject * theChangedSubject){    for(int i = 0; i < _size; i++){        if(theChangedSubject == _subject[i]){
            DealClientConnect_m(_subject[i]);
        }
    }
}void ServerTCPConnect_m::DealClientConnect_m(Subject *s){    if(ClientTCPConnect *tcp = dynamic_cast<ClientTCPConnect*>(s)){
        tcp->GetData();//deal with TCP Data        ...
    }
    .
    .
    .    else if(ClientTCPConnect_n *tcp = dynamic_cast<ClientTCPConnect_n*>(s)){
        tcp->GetData_n();//deal with TCP Data        ...
    }
}

移动开发培训,Android培训,安卓培训,手机开发培训,手机维修培训,手机软件培训

 

  观察者模式模型图

  说了这么多,有时不如一张图来的直接,观察者模式可以用下UML图来表示,清晰明了!

 移动开发培训,Android培训,安卓培训,手机开发培训,手机维修培训,手机软件培训

                  观察者模式UML图b)

 

  著名的MVC(Model/View/Controller)模式也是基于OBSERVER设计模式的,Model类担任目标的角色,而View是观察者的基类,Controller用于控制应用程序的流程,它处理事件并作出响应。“事件”包括用户的行为和数据 Model 上的改变。

  移动开发培训,Android培训,安卓培训,手机开发培训,手机维修培训,手机软件培训

  MVC组件之间的典型合作c)

     观察者模式的优点是只要订阅/登记了之后,当目标改变时,观察者能自动更新。

 

http://www.cnblogs.com/chenyangchun/p/6872060.html

延伸阅读

告别“老顽固”-Java培训,做最负责任的教育,学习改变命运,软件学习,再就业,大学生如何就业,帮大学生找到好工作,lphotoshop培训,电脑培训,电脑维修培训,移动软件开发培训,网站设计培训,网站建设培训告别“老顽固”