|
科普文,给大家介绍观察者模式的使用场合及其优缺点。
模式动机
观察者模式是比较常用的设计模式之一,尤其是系统里面涉及到多个复杂子系统时,经常会使用到。 它就像系统里面某个子模块的跑腿,一旦该子模块发生变化,它就要为这个子模块通知其他的子模块。 一个经典的例子就是我们操作系统所使用到的GUI界面,当我们在GUI系统里面使用各种应用程序时,只需要用鼠标轻轻点击软件右上方的全屏/非全屏,软件里面全部的组件就会进行相应的缩放,这里面使用到的就是观察者模式。 观察者模式定义:
场景案例 在单片机开发里面,串口通信是很重要的通信手段。在业务代码里面,有很多子模块都关注着串口通信的数据。假设在串口数据来临的时候,我们需要去通知各个子模块。 伪代码实现: //串口中断urat_isr() { ... //通过设置全局变量来通知子模块一 notify_module1 = 1; //通过设置全局变量来通知子模块二 notify_module2 = 1; //通过设置全局变量来通知子模块三 notify_module3 = 1; ... } ... //主函数,创建多个线程来处理不同任务 int main() { ... //线程1(子模块一) create_thread1(); //线程2(子模块二) create_thread2(); //线程3(子模块三) create_thread3(); ... } 在上面的代码实现中,串口数据发生更新时,通过给各个全局变量置1来通知各个子模块。等到各个子模块得到运行机会后,判断并更新串口通信数据。 在这个实现方案中,串口数据通过全局变量来通知子模块的方式非常死板,一旦需要通知的子模块发生变化,必须要改动串口中断部分代码。
改进方案 在多个子系统同时监视某一个子系统时,应该添加一个观察者模块,来解开通信引起的子系统耦合。 伪代码实现: //定义观察对象的数量#define num 3 //定义观察对象 typedef struct object { //定义观察对象的通知接口 void (*update)(); }Object; //定义观察者模块 typedef struct observer { Object* objectList[num]; }Observer; //定义一个观察者模块 Observer aobserver; //主函数,创建多个线程来处理不同任务 int main() { //初始化各个观察对象 aobserver.objectList[0]->update = Update_module1; aobserver.objectList[1]->update = Update_module2; aobserver.objectList[2]->update = UpdateUpdate_module3; ... //线程1(子模块一) create_thread1(); //线程2(子模块二) create_thread2(); //线程3(子模块三) create_thread3(); ... } //串口中断 urat_isr() { ... for(i = 0; i < num; i++) aobserver.objectList->update(); ... } 各个子模块的通知接口,可以像这样子来实现: //module1通知接口void Update_module1() { //通过设置全局变量来通知子模块一 notify_module1 = 1; } //module2通知接口 void Update_module2() { //通过设置全局变量来通知子模块一 notify_module2 = 1; } //module3通知接口 void Update_module3() { //通过设置全局变量来通知子模块一 notify_module3 = 1; } 总结 这就是c语言中的观察者模式,它可以动态地增加、减少观察对象,解除子模块间的直接耦合,可以很好地预防程序需求发生变化。 但是在实际使用过程中,需要考虑一下开发效率和运行效率问题:
|
微信公众号
手机版