设计模式之观察者模式

2018-09-21 20:57:03ios

观察者模式

  观察者模式又叫作发布-订阅(Publish/Subscribe)模式。它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知全部的观察者对象,使它们可以自动更新本身。框架

观察者(Observer)模式UML类图

Subject类(通常叫作主题或者抽象统治者,一般用一个抽象类实现),它把全部对观察者对象的引用保存在一个集合里,每一个主题均可以有任何数量的观察者。抽象主题提供一系列方法,能够增长和删除观察者对象。ide

Observer类,抽象观察者,为全部的具体观察者定义一个接口,在获得主题的通知时更新本身。更新方法一般包含一个Update()方法。学习

ConcreteSubject类,具体主题或者具体通知者,将有关状态存入具体观察者对象;在具体主题的内部状态改变时,给全部登记过的观察者发出通知。spa

ConcreteObserver类,具体观察者,实现抽象观察者角色所要求的更新接口,以便使自己的状态与主题的状态相协调。一般用一个具体子类实现。3d

代码示例

  还读书的时候,有节课咱们都很喜欢,就是自习课,由于你们能够在自习课上作不少有意思的事情,好比睡觉、看小说、玩手机等等。但是万一被老师抓到了那可就麻烦了,玩手机的手机会被没收,看小说的小说也要上交,要是睡觉,那你中大奖了,可能要被罚站。因此怎么办呢,若是老师来了,有人通知,那么就不会被罚了,只要及时作好认真学习的样子(^_^)。这里就隐含了一个可使用观察者模式的场景,在这个场景中,老师就是要订阅的主题,班级里没好好学习的学生就是观察者。code

1.抽象观察者(每一个观察者都必需要有一个Update方法,或者功能相似的方法,方法的功能就是根据订阅主题的变化,本身作出相应的动做)(UML中的Observer)server

#ifndef OBSERVER_H_
#define OBSERVER_H_
#include <string>
class Observer
{
public:
    virtual void update(std::string strTeacherName) = 0;
    Observer() = default;
    virtual ~Observer() = default;
};
#endif
Observer

2.具体观察者对象

#ifndef PLAYINGSTUDENT_H_
#define PLAYINGSTUDENT_H_

#include "Observer.h"
#include <iostream>
#include <string>
class PlayingStudent:public Observer
{
public:
    void update(const std::string strTeacherName) override;
    PlayingStudent(const std::string strStudentName) : m_strNameOfStudent(strStudentName){};
    ~PlayingStudent() = default;
private:
    std::string m_strNameOfStudent;
};
#endif

#include "PlayingStudent.h"

void PlayingStudent::update(const std::string strTeacherName)
{
    std::cout << "Subject: "<< strTeacherName << " is coming!" << std::endl;
    std::cout << "Observer:" << m_strNameOfStudent << " :Where ?" << std::endl;
}

#ifndef SLEEPINGSTUDENT_H_
#define SLEEPINGSTUDENT_H_

#include "Observer.h"
#include <iostream>
#include <string>
class SleepingStudent:public Observer
{
public:
    void update(const std::string strTeacherName) override;
    //Construct Function: or you can declare it as Observer(ConcreteSubject objConcretestruct,const std::string strName) for get state of Subject
    SleepingStudent(const std::string strStudentName):m_strNameOfStudent(strStudentName){};
    ~SleepingStudent() = default;
private:
    std::string m_strNameOfStudent;
};
#endif

#include "SleepingStudent.h"

void SleepingStudent::update(const std::string strTeacherName)
{
    std::cout << m_strNameOfStudent << "," << strTeacherName << "is comming,weak up" << std::endl;
}
ConcreteOberver

3.抽象主题blog

#ifndef SUBJECT_H_
#define SUBJECT_H_
#include "Observer.h"

class Subject
{
public:
    virtual void attach(Observer *) = 0;  //Add Observer
    virtual void detach(Observer *) = 0;  //Remove Observer
    virtual void notify() = 0;        //Note Observer
    Subject() = default;
    virtual ~Subject() = default;
};
#endif
Subject

4.具体主题

#ifndef CONCRETESUBJECT_H_
#define CONCRETESUBJECT_H_

#include "Subject.h"
#include "Observer.h"
#include <list>
#include <string>
class ConcreteSubject : public Subject
{
public:
    void attach(Observer* objObserver) override;
    void detach(Observer *objObserver) override;
    void notify() override;
    void setTeacherName(const std::string strName)
    {
    m_strNameOfTeacher = strName;
    }
    ConcreteSubject() = default;
    ~ConcreteSubject() = default;
private:
    std::string m_strNameOfTeacher;
    std::list<Observer* > m_listObserver;
};
#endif

#include "ConcreteSubject.h"

void ConcreteSubject::attach(Observer* objObserver)
{
    m_listObserver.push_back(objObserver);
}

void ConcreteSubject::detach(Observer *objObserver)
{
    m_listObserver.remove(objObserver);
}

void ConcreteSubject::notify()
{
    for(auto value : m_listObserver)
    {
    value->update(m_strNameOfTeacher);
    }
}
ConcreteSubject

5.client

#include "ConcreteSubject.h"
#include "SleepingStudent.h"
#include "PlayingStudent.h"
using namespace std;

int main(int argc,char *argv[])
{
   SleepingStudent objStudentA("H&T");
   ConcreteSubject objSubject;
   objSubject.setTeacherName("Mr Su");
   objSubject.attach(&objStudentA);
   
   SleepingStudent objStudentB("M");
   objSubject.attach(&objStudentB);

   PlayingStudent objPlayingStudentA("N");
   objSubject.attach(&objPlayingStudentA);
   
   PlayingStudent objPlayingStudentB("Z");
   objSubject.attach(&objPlayingStudentB);
   objSubject.detach(&objPlayingStudentB);
   objSubject.notify();
   return(1);
}
Client

观察者模式的特色

适用场景:

  将一个系统分隔成一系列相互协做的类带来的一个反作用是:须要维护相关对象的一致间的一致性。咱们不但愿为了维护一致性而使得各种紧密耦合,这样会给维护、扩展和重用都带来不便。而观察者模式的关键对象是主题Subject和观察者Observer,一个Subject能够有任意数目的依赖它的Observer,一旦Subject的状态发生了改变,全部的Observe均可以获得通知。Subject发出通知时并不须要知道谁是它的观察者,也就是说,具体观察者是谁,它根本不须要知道。而任何一个具体观察者也不须要知道其它观察者的存在。

  那么当一个对象的改变须要同时改变其它对象的时候,而且它不知道具体有多少对象有待改变时,应该考虑使用观察者模式。就是说,一个抽象模型有两个方面,其中一方面依赖于另外一方面,这时用观察者模式能够将这二者封装在独立的对象中使它们各自独立的改变和复用。

  观察者所作的事情就是解耦合,让耦合的双方都依赖于抽象,而不依赖具体,从而使得各自的变换都不会影响另外一边的变化。

优势:

  1.一个抽象模型的两个方面相互依赖,使用观察者模式将其解耦为观察者和主题,配合使用虚基类,使得二者的耦合度下降(依赖抽象而不依赖具体),这能使双方的变化能够独自进行以及复用。

缺点:

  1.若是一个主题有不少个观察者,将全部观察者挨个通知一遍会消耗时间

  2.观察者模式没有一个机制让观察者知道变化是怎么发生的,而仅知道发生了这样的变化。

       3.观察目标和观察者之间若是有循环依赖的话(观察者和观察目标彼此拥有一个存储对方对象的集合),观察目标会触发两个模块之间的循环调用,这有可能会引起系统崩溃

  4.虽然经过抽象观察者和抽象主题,解除了实现之间的依赖,可是依然存在抽象观察和抽象主题这样的依赖,对一个已经开发好的框架,假设这两个缺乏其一,那么就不能添加主题的通知功能,另外观察者接到通知后的方法名可能有不少种,可是通知者和观察者彼此之间并不知道, 此时能够由客户端来决定通知谁。

相关文章
相关标签/搜索