常见设计模式之(五):观察者模式
文章目录
常见设计模式之(五):观察者模式
1 什么是观察者模式(observer pattern)?
1.1 定义
摘自 java 设计模式官网 中 有关观察者模式的定义:
Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically
翻译一下:
定义这样一种一对多的关系:当一个对象状态改变时,所有依赖这个对象的都会得到通知并且自动更新。
1.2 观察者模式中角色
角色 | 职责 |
---|---|
被观察者(subject) | 1. 动态地增加、取消观察者;2、发生改变时通知观察者 3、一般为抽象类 |
具体观察者(ConcreteSubject) | 1. 实现自己的业务逻辑(比如说获取串口数据等)2、定义对哪些事件进行通知 |
观察者(Observer) | 1、收到Update时,对收到的信息进行处理 |
具体观察者(ConcreteObserver) | 1、对于不同的具体的观察者,对于信息的处理是不一样的 ,每个具体的观者都有自己的处理逻辑 |
2、观察者模式的c++实现
本博文先介绍观察者模式最简单模型,一般通用的类图如下:
c++源码如下:
AbstractSubject.h
#ifndef ABSTRACT_SUBJECT_H
#define ABSTRACT_SUBJECT_H
#include <stdio.h>
#include <vector>
#include "AbstractObserver.h"
class AbstractObserver;
class AbstractSubject
{
public :
AbstractSubject()
{
}
~AbstractSubject()
{
}
void add_observer(AbstractObserver * observer)
{
vec_observer.push_back(observer);
}
void delete_observer(AbstractObserver * observer)
{
for(auto intor = vec_observer.begin();intor != vec_observer.end();intor++)
{
if(*intor == observer)
{
vec_observer.erase(intor);
}
}
}
void notify_all_observer(void)
{
for(auto intor = vec_observer.begin();intor != vec_observer.end();intor++)
{
(*intor) ->update();
}
}
private:
std::vector< AbstractObserver *> vec_observer;
};
#endif
AbstractObserver.h
#ifndef ABSTRACT_OBSERVER_H
#define ABSTRACT_OBSERVER_H
#include <stdio.h>
#include <vector>
class AbstractObserver
{
public :
AbstractObserver()
{
}
~AbstractObserver()
{
}
virtual void update(void) = 0;
};
#endif
ConcreteSubject.h
#ifndef CONCRETE_SUBJECT_H
#define CONCRETE_SUBJECT_H
#include <stdio.h>
#include "AbstractSubject.h"
class ConcreteSubject : public AbstractSubject
{
public:
ConcreteSubject()
{
}
~ConcreteSubject()
{
}
void do_some_thing(void)
{
printf("ConcreteSubject %s %d \n",__FUNCTION__,__LINE__);
notify_all_observer();
}
};
#endif
ConcreteObserver1.h
#ifndef CONCRETE_OBSERVER_1_H
#define CONCRETE_OBSERVER_1_H
#include "AbstractObserver.h"
class ConcreteObserver1 :public AbstractObserver
{
public:
ConcreteObserver1()
{
}
~ConcreteObserver1()
{
}
virtual void update(void) override
{
printf("%s %s %d \n",__FILE__,__FUNCTION__,__LINE__);
}
};
#endif
ConcreteObserver2.h
#ifndef CONCRETE_OBSERVER_2_H
#define CONCRETE_OBSERVER_2_H
#include "AbstractObserver.h"
class ConcreteObserver2 :public AbstractObserver
{
public:
ConcreteObserver2()
{
}
~ConcreteObserver2()
{
}
virtual void update(void) override
{
printf("%s %s %d \n",__FILE__,__FUNCTION__,__LINE__);
}
};
#endif
ConcreteObserver3.h
#ifndef CONCRETE_OBSERVER_3_H
#define CONCRETE_OBSERVER_3_H
#include "AbstractObserver.h"
class ConcreteObserver3 :public AbstractObserver
{
public:
ConcreteObserver3()
{
}
~ConcreteObserver3()
{
}
virtual void update(void) override
{
printf("%s %s %d \n",__FILE__,__FUNCTION__,__LINE__);
}
};
#endif
ObserverClient.cpp
#include "AbstractSubject.h"
#include "AbstractObserver.h"
#include "ConcreteObserver1.h"
#include "ConcreteObserver2.h"
#include "ConcreteObserver3.h"
#include "ConcreteSubject.h"
int main(void)
{
// 定义一个被观察者
AbstractSubject * pcls_subject = new ConcreteSubject();
// 定义三个观察者
AbstractObserver * co1 = new ConcreteObserver1();
AbstractObserver * co2 = new ConcreteObserver2();
AbstractObserver * co3 = new ConcreteObserver3();
//添加三个观察者
pcls_subject->add_observer(co1);
pcls_subject->add_observer(co2);
pcls_subject->add_observer(co3);
// 被观察者状态改变
pcls_subject->notify_all_observer();
pcls_subject->delete_observer(co2);
pcls_subject->notify_all_observer();
delete co1;
delete co2;
delete co3;
delete pcls_subject;
}
编译并运行如下:
aaa@qq.com:/wan/06Observer# g++ -o ObserverClient ObserverClient.cpp -std=c++11 -g
aaa@qq.com:/wan/06Observer# ./ObserverClient
ConcreteObserver1.h update 22
ConcreteObserver2.h update 22
ConcreteObserver3.h update 22
ConcreteObserver1.h update 22
ConcreteObserver3.h update 22
3 观察者模式的应用
3.1 观察者模式的注意事项
广播链问题
在观察者模式中一个观察者可以拥有双重身份,既是观察者,也是观察者,这就意味着:A -> B ->C 一旦建立这样的广播链,会造成程序负担。如果不慎就会广播风暴。
程序效率
在存在多级触发时会存在程序的效率问题。
3.2 实际项目中具体应用
- 文件系统 :比如说 我们建立了一个新文件,在建立文件的同时需要通知 磁盘管理 目录管理器等程序
- 生活中的收音机
- ATM机:当我们取完钱时:通知短信系统、余额更新系统等
4 观察者模式的扩展
4.1 项目中真实的观察模式
上述例子是非常原始的观察者模式,而在我们实际的项目中不会存在这样纯粹的模式,在系统设计中会对观察者中各个角色进行改造。
4.1.1 观者者和被观察者之间的沟通机制
被观察者状态改变会触发观察者的一个行为,同时会传递一个消息。这样就存在两个问题:如何通知给观者者,传递的消息的形式。
- 如何传递:
观者和被观察者可以通过以下技术传递消息:消息队列、共享内存、管道、网络、硬件总线等,大家在进行系统设计时可以根据自己的情况进行选择。 - 消息传递格式:
常见的消息格式有以下几种:json xml protobuf 自定义数据格式等。
4.1.2 观察者响应
观察者在既要接受被观察者传递过来的消息,同时还要处理相关消息,在观察者要处理N个被观察者传递过来的消息时,观察者就需要考虑自己的响应效率,一般两种方案:
1.空间换时间
采用缓存技术,准备好足够的空间来缓存消息,我先将消息缓存下来,然后自己慢慢处理,我只需要保证及时、快速地响应就可以了。
2.利用多线程技术
增加线程的数量,增强自己系统能力。
4.1.3 被观察者选择通知的人
在实际的项目中,存在这样问题,被观察者可能不需要传递给所有人,只需要传递给那些对消息感兴趣的的人,这点有点像网络中 广播 组播 单播 。被观察者自己决定传递给哪些人。做系统设计时需要考虑这个问题,保证架构的灵活性。
4.1.4 观察者和被观察者的扩展
首先是角色的变化:
双重身份的角色的存在。一个观察者同样也可以是被观察者。
数量的变化:
多个观察者 多个被观察者。
4.2 观者模式的终极boss (订阅-发布模式)
通过上面对观察者模式的扩展,最终扩展为订阅-发布的模式,想想生活中这样的例子太多了:手机消息的推送、公众号的订阅等等。在我们实际的开发中也存在一些订阅-发布模式的开源工具:比如说 mqtt zeromq nanomq等工具都支持订阅发布,在后续博文中我们将以mqtt为例来讲述订阅发布在实际项目中的应用。
下一篇: vue引入swiper插件的使用实例