zoukankan      html  css  js  c++  java
  • 程序设计模式 —— 观察者模式

     什么是观察者?


    观察者模式顾名思义就是很多个 类观察主要的类,如果主要的类一旦触发事件,就会通知所有的 观察者类。

    如果你不理解观察者模式,我将用一些比较简单的说明来让你理解。

    如图

    这样说来,所有订报纸的就是观察者,报社就是线程,报社(线程)触发了事件之后会告诉卖报纸(主题)的,叫他去送报纸(发送事件).然后我们观察者就之后了

    这有什么用?


    或许有人在学习新知识之前,会问这玩意有什么用,其实我个人认为,观察者设计模式一般用于监听鼠标事件,键盘事件或者其他自定义事件。

    因为我们基本上都是写好一个函数,然后等待事件就好了。

    那么。代码应该如何实现呢?由于在《head First》 里面已经有Java的实现了,所以我就用C++语言写了一下。

    框架图


    首先大致框架图:

    大致如上

    其中Shell就是 卖报的。所有 卖报的就要实现 Subject 接口(一般而言就一个,当然了你也可以多对多)

    Shellp pash ids 都是订报纸的。所有要定报纸的都必须实现 Observe 接口

    代码如下:

    注意要加上:

    1 #include <iostream>
    2 #include <vector>  //可以去搜索一下  vector 如何用,如果你熟悉java,可认为它是 ArrayList 类
    3 
    4 using namespace std;

    具体代码实现


    首先我们先实现 基类

    1 /*观察者抽象基类*/
    2 class Observer
    3 {
    4 public:
    5     //这里的参数 之所以用string 是为了告诉你可以类传递,甚至也可以传递其他
    6     virtual void updata(const char* str) = 0;
    7     //注意,在你真实项目中用的时候,应该写一个虚的析构函数
    8 
    9 };
     1 /*主题 抽象基类*/
     2 class Subject
     3 {
     4 public:
     5 
     6     virtual void eventObserver() = 0;    //事件触发!其他程序来用
     7     virtual void registerObserver(Observer *) = 0;    //注册成为观察者
     8     virtual void removeObserver(Observer *) = 0;    //管理者删除自己
     9 
    10 };

    好了,基本上是这样。那么我们来实现主题:继承subject

     1 /*Shell 继承主题*/
     2 class Shell : public Subject
     3 {
     4 private:
     5     std::vector <Observer*> list;
     6 
     7 public:
     8     //实现方法
     9     inline virtual void eventObserver()
    10     {
    11         vector<Observer*>::iterator it;
    12         int z=0;
    13         for (it = list.begin(); it < list.end();it++,z++)    //代送器遍历
    14         {
    15 
    16             (*it)->updata("Hello!");   //向每一个 观察者(监听者) 触发Observe 基类的函数,相当于发送事件
    17         }
    18     };
    19     inline virtual void registerObserver(Observer * ob)//注册成为观察者
    20     {
    21         list.push_back(ob);//add
    22     };
    23     inline virtual void removeObserver(Observer * ob)
    24     {
    25         vector<Observer*>::iterator it;
    26         int z=0;
    27         for (it = list.begin(); it < list.end();it++,z++)    //代送器遍历
    28         {
    29             if (&ob == &*it){
    30                 //如果地址相同 删除
    31                 list.erase(list.begin()+z);
    32             }
    33         }
    34     };
    35 };

    然后我们如概念图,我们节约时间,只实现两个 观察者(相当于就是 事件的监听者)

     1 /*Dis 观察者*/
     2 class Dis : public Observer
     3 {
     4 public:
     5     inline virtual void updata(const char* str)  //这个是Observe基类的方法,这里对其实现,主题(Shell类)会调用这个方法。触发事件
     6     {
     7         cout << "[Dis] DataGet :"<< str << endl;
     8     };
     9 };
    10 
    11 
    12 /*Pash 观察者*/
    13 class Pash : public Observer
    14 {
    15 public:
    16     inline virtual ~Pash()
    17     {
    18 
    19     };
    20     inline Pash()
    21     {
    22 
    23     };
    24     inline virtual void updata(const char* str)   //这个是Observe基类的方法,这里对其实现,主题(Shell类)会调用这个方法。触发事件
    25     {
    26         cout << "[Pash] DataGet :" << str << endl;
    27     };
    28 };

    上面分别写了两个类,两个类都实现了 updata函数。而且有不同的输出(功能)。

    下面我们来运行试试?

     1 /*主函数*/
     2 int main()
     3 {
     4     Pash *pash = new Pash();
     5     Dis *dis = new Dis();
     6     Dis *dis2 = new Dis();
     7     Dis *dis3 = new Dis();
     8     Shell shell;
     9     //初始化
    10 
    11     shell.registerObserver(pash);    //注册
    12     shell.registerObserver(dis);
    13     shell.registerObserver(dis2);
    14     shell.registerObserver(dis3);
    15 
    16     shell.removeObserver(dis3);    //删除dis3 
    17 
    18     //触发事件
    19     shell.eventObserver();    //这一行不一定非得写在这里,可以在任何需要触发的地方(其他线程)触发
    20 
    21     //这一行无视~我是用Sublime写的,所以加个这个东西看输出
    22     std::cin.get();
    23     return 0;
    24 }

    输出:

    那么到现在为止,我们到底做了什么?


    可以看见,这一行:

    将 Observe 的子类 Dis(观察者)注册到了 Shell 类(主题)中。并且随时可以等待事件,所有的都一样。

    1 shell.registerObserver(dis);


    从其他的地方激活事件,然后shell类(主题)就会告诉所有的已经注册了的 Observe 的子类(观察者),触发 “ virtual void updata(const char* str) ”函数;

    1  shell.eventObserver();    //这一行不一定非得写在这里,可以在任何需要触发的地方(其他线程)触发

    怎么触发的呢?

    可以去看看Shell类的16行 “ (*it)->updata("Hello!"); ” 对每个 已经注册了的 Observe 子类进行触发。

    至此,这就是观察者设计模式。

    最后


    但是要注意的一点,不是说有了这个观察者模式,就必须要加进去使用,程序会更棒。

    设计模式要与你的程序相互和谐,不能写个 “HelloWorld” 程序都用到了设计模式。

    总的一句话,设计模式不是规则,而是你随时可以改变的模式。这也是很多设计模式书籍强调的一点。

    <Thanks>不论对你是否有帮助,还是谢谢您的耐心查看。如有错误之处,还望指教。</Thanks>

  • 相关阅读:
    一种C#读写二进制文件的通用方法
    关于POP3协议的一点资料
    关于看图工具的几点想法
    在WPF程序中将控件所呈现的内容保存成图像
    Nuget挂了的解决方法
    VisualStudio 2012中的单元测试
    在Andorid平板上体验Windows8的猜想
    创建自己的awaitable类型
    【转载】:最佳注释
    百度云盘试用
  • 原文地址:https://www.cnblogs.com/suwings/p/5903326.html
Copyright © 2011-2022 走看看