zoukankan      html  css  js  c++  java
  • 观察者模式

    观察者模式的UML类图入下 :

    解决的问题 :

    • 解耦,参考QT的信号槽机制

    详细描述:

    • 一个通知者有一份观察者的名单,通知者状态改变时,去名单上通知所有的观察者

    注意点:

    • 通常开发中update()方法的名字不是固定的,会很不方便,C#事件委托机制
    • c++中建议使用sigslotsigc++等信号槽库

    例子:

    1. QT信号槽
    2. 前台小妹(通知者)发现老板回来,于是通知员工们(观察者们)把状态改变为“认真工作”状态
    3. 红外摄像头(通知者)发现温度变化,于是通知观察者们【告警窗口】,【警铃】,【日志记录】和【断电按钮】执行自己的动作

    代码:

    1. 抽象通知者Subject.hpp

      • 拥有一个名单set<Observer*> obs,存所有要通知的人(观察者们)
      • 添加通知者函数,移除通知者函数
      • 更新状态函数,遍历所有观察者,执行他们的update()函数
          #ifndef _SUBJECT_H 
          #define _SUBJECT_H 
           
          #include "MajiaoObject.hpp" 
          #include "Observer.hpp"
          #include <set>
           
          class Subject : virtual public MajiaoObject { 
              public :  
                  // 观察者列表
                  set<Observer*> obs; 
                  Subject() {  } 
                  ~Subject() {  } 
                  virtual Subject* addObserver(Observer* ob) {
                      this->obs.insert(ob);
                      return this;
                  }
          
                  virtual Subject* delObserver(Observer* ob) {
                      this->obs.erase(ob);
                      return this;
                  }
          
                  // 遍历所有观察者,并执行各自的更新操作
                  virtual void notify(int status) {
                      for(set<Observer*>::const_iterator it = obs.begin(); it != obs.end(); it ++) {
                          (*it)->update(status);
                      }
                  }
          }; 
          #endif	// _SUBJECT_H
        
    2. 抽象观察者Observer.hpp,拥有update()函数

      #ifndef _OBSERVER_H 
      #define _OBSERVER_H 
       
      #include "MajiaoObject.hpp" 
       
      class Observer : virtual public MajiaoObject { 
          public :  
              Observer() {  } 
              ~Observer() {  } 
              virtual void update(int status) { }
      }; 
      #endif	// _OBSERVER_H
      
    3. 具体通知者子类TempCamera.hpp,发现温度异常就去通知所有TempAlertWindow

      #ifndef _TEMPCAMERA_H 
      #define _TEMPCAMERA_H 
       
      #include "Subject.hpp" 
       
      class TempCamera : virtual public Subject { 
          public :  
              GET_SET(public, double, temp);
              double TEMP_LIMIT = 38.5;
      
              TempCamera() {  } 
              ~TempCamera() {  } 
      
              virtual void catchTemp() {
                  if (temp > TEMP_LIMIT) {
                      int statusError = 1;
                      // 通知列表里的所有观察者
                      this->notify(statusError);
                  }
              }
      }; 
      #endif	// _TEMPCAMERA_H
      
    4. 具体观察者TempAlertWindow.hpp,被通知时执行相应动作报警窗口变红色

      #ifndef _TEMPALERTWINDOW_H 
      #define _TEMPALERTWINDOW_H 
       
      #include "../AlertTemplate.hpp" 
      #include "../../Observer.hpp"
      #include <string.h>
      #include <string>
      using namespace std;
      
      // 这里产生了菱形继承问题,需要用virtual public解决
      class TempAlertWindow : virtual public AlertTemplate, virtual public Observer { 
          
              GET_SET(public, double, temp);
              GET_SET(public, int, windowColor);
      
              TempAlertWindow() {  } 
              ~TempAlertWindow() {  } 
      
              virtual void buildAlert() {
                 this->alertMsg = "温度异常 ";
                 this->alertMsg.append(to_string(this->temp));
                 this->alertMsg.append(" 度"); 
              }
              virtual void sendAlert() { }
              virtual void callLeaders() { }
              virtual void closeAlert() { } 
              void windowRed() { this->windowColor = 0xFF0000; }
      
              virtual void update(int status) override {
                  this->buildAlert();
                  cout << this->getid() << "  " << this->alertMsg << endl;
              }
      }; 
      #endif	// _TEMPALERTWINDOW_H
      
    5. 使用sigslot库来模拟

      1. 模拟传感器发现温度异常,发出信号,日志对象接收信号,并执行动作
      2. 通知者需要一个成员变量sigslot::signal1<string> sig
        #ifndef _TEMPTRANSDUCER_H 
        #define _TEMPTRANSDUCER_H 
        
        #include "./sigslot/sigslot.h"
        #include "MajiaoObject.hpp" 
        
        class TempTransducer : virtual public MajiaoObject { 
            public :  
                // 使用sigslot库,只需要一个信号成员变量
                sigslot::signal1<string> sig;
        
                TempTransducer() {  } 
                ~TempTransducer() {  } 
        
                virtual void catchTemp() {
                    string str = "传感器 ";
                    str.append(to_string(this->getid())).append(" 发现温度异常");
                    this->sig.emit(str);
                }
        }; 
        #endif	// _TEMPTRANSDUCER_H
        
      3. 观察者方需要继承sigslot::has_slot<>,并且函数返回值是void,参数和信号方匹配
        #ifndef _ALERTLOGGER_H 
        #define _ALERTLOGGER_H 
         
        #include "../sigslot/sigslot.h"
        #include "../MajiaoObject.hpp" 
         
        // 这里如果虚继承sigslot::has_slots<>报错
        class AlertLogger : virtual public MajiaoObject, public sigslot::has_slots<> { 
            public :  
                AlertLogger() {  } 
                ~AlertLogger() {  }
        
                // 返回值是void,参数和信号方匹配
                virtual void log(string str) {
                    cout << "记录日志 " << str << endl;
                }
        }; 
        #endif	// _ALERTLOGGER_H
        
      4. main里,需要连接信号槽
        TempTransducer* tran = new TempTransducer();
        AlertLogger* logger2 = new AlertLogger(),
                   * logger3 = new AlertLogger();
        logger2->setid(2); 
        logger3->setid(3);
        
        // 绑定信号和槽
        tran->sig.connect(logger2, &AlertLogger::log);
        tran->sig.connect(logger3, &AlertLogger::log);
        // 发射信号
        tran->sig.emit("温度38度");
        tran->catchTemp();
        
  • 相关阅读:
    pytest+allure生成测试报告
    pytest之fixture使用详解
    pytest框架介绍
    使用records库操作SQL并且查询MySQL数据库
    python模块之codecs
    项目总结
    第二阶段团队绩效评分
    软件发布2.0
    “随手记”开发记录day20
    “随手记”开发记录day19
  • 原文地址:https://www.cnblogs.com/majiao61/p/15055676.html
Copyright © 2011-2022 走看看