观察者模式的UML类图入下 :
解决的问题 :
- 解耦,参考QT的信号槽机制
详细描述:
- 一个通知者有一份观察者的名单,通知者状态改变时,去名单上通知所有的观察者
注意点:
- 通常开发中
update()
方法的名字不是固定的,会很不方便,C#
有事件委托机制
- c++中建议使用
sigslot
或sigc++
等信号槽库
例子:
- QT信号槽
- 前台小妹(通知者)发现老板回来,于是通知员工们(观察者们)把状态改变为“认真工作”状态
- 红外摄像头(通知者)发现温度变化,于是通知观察者们【告警窗口】,【警铃】,【日志记录】和【断电按钮】执行自己的动作
代码:
-
抽象通知者
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
- 拥有一个名单
-
抽象观察者
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
-
具体通知者子类
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
-
具体观察者
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
-
使用
sigslot
库来模拟- 模拟传感器发现温度异常,发出信号,日志对象接收信号,并执行动作
- 通知者需要一个成员变量
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
- 观察者方需要继承
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
- 在
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();