|
科普文,给大家介绍观察者模式的使用场合及其优缺点。
模式动机
观察者模式是比较常用的设计模式之一,尤其是系统里面涉及到多个复杂子系统时,经常会使用到。 它就像系统里面某个子模块的跑腿,一旦该子模块发生变化,它就要为这个子模块通知其他的子模块。 一个经典的例子就是我们操作系统所使用到的GUI界面,当我们在GUI系统里面使用各种应用程序时,只需要用鼠标轻轻点击软件右上方的全屏/非全屏,软件里面全部的组件就会进行相应的缩放,这里面使用到的就是观察者模式。 观察者模式定义:
场景案例 在单片机开发里面,串口通信是很重要的通信手段。在业务代码里面,有很多子模块都关注着串口通信的数据。假设在串口数据来临的时候,我们需要去通知各个子模块。 伪代码实现: //串口中断( ^( m; I. _' u2 s/ h j) s0 n; Xurat_isr(): O6 p- o1 I* u) U! U* I3 o( s { ... //通过设置全局变量来通知子模块一 notify_module1 = 1; //通过设置全局变量来通知子模块二 notify_module2 = 1; //通过设置全局变量来通知子模块三 notify_module3 = 1;4 l5 d5 f0 }, X5 ]$ ?, ] ... }) V4 R- { M( ^4 z7 r! d2 D: V( P ...$ d' N3 h% m* E4 E% q: L# j# a; C& | //主函数,创建多个线程来处理不同任务% `& X' {( K$ e% h; U int main() {7 A; ?6 ]9 A$ ], T, R1 n ...! C( a8 H5 A a N5 P //线程1(子模块一) create_thread1();/ a0 c& B5 m; q //线程2(子模块二)# W |* `# K: I) v8 [" g/ I% R create_thread2(); //线程3(子模块三) create_thread3();/ Y ` G; \1 n; |; Y3 z6 x' z ... }* ~8 [8 @, ?& x' t( ^ 在上面的代码实现中,串口数据发生更新时,通过给各个全局变量置1来通知各个子模块。等到各个子模块得到运行机会后,判断并更新串口通信数据。 在这个实现方案中,串口数据通过全局变量来通知子模块的方式非常死板,一旦需要通知的子模块发生变化,必须要改动串口中断部分代码。
改进方案6 d u8 c' W" x1 i- T- p 4 c! A/ ]4 @9 n- A6 M 在多个子系统同时监视某一个子系统时,应该添加一个观察者模块,来解开通信引起的子系统耦合。 伪代码实现: //定义观察对象的数量#define num 3 g7 l; X2 J0 g# f% e ' k. A6 z3 [5 w+ D4 E //定义观察对象 typedef struct object { //定义观察对象的通知接口1 g8 F7 ~. b; m6 _* n5 T0 A void (*update)(); }Object;7 |" |3 [$ M) u0 Q: o //定义观察者模块 typedef struct observer {; E3 S$ h7 g `% Q# }( c Object* objectList[num];# h( X1 }, t8 x }Observer;0 o% S/ w4 E" \5 F $ | ]$ I* I* H3 n: Q, Q //定义一个观察者模块+ h0 X1 h; \, a2 a& D1 ^) i Observer aobserver; //主函数,创建多个线程来处理不同任务 int main()5 N" Z2 Y- r! p& X3 m {$ ~ ?5 O& z! v# L3 _& V //初始化各个观察对象 aobserver.objectList[0]->update = Update_module1; aobserver.objectList[1]->update = Update_module2; aobserver.objectList[2]->update = UpdateUpdate_module3; ...; J' O! s3 ^8 a0 d- f //线程1(子模块一)$ z8 F6 F: K7 Y3 o$ x create_thread1(); //线程2(子模块二)+ o" v' g3 C/ j% |5 X9 F create_thread2(); //线程3(子模块三) create_thread3(); ...& q% }9 y6 V( s& f / L3 Q, M& w8 I }6 ]' n" _. P$ U //串口中断& {+ p' Q- H$ W& n urat_isr()5 O. D- Z, r1 j1 N \5 S- Z# t {' R$ M) {$ n6 J6 v V' p5 \9 ]; h ... for(i = 0; i < num; i++)% t% U( I5 a& d. K* M, Y# H1 D aobserver.objectList->update();& d! ?$ e. F# I4 R' X ...- |8 y, _& Z- ~$ i" V: D } * x$ u1 `* N% S 各个子模块的通知接口,可以像这样子来实现: //module1通知接口void Update_module1() {; Z) T+ |9 h* U# k) L7 f //通过设置全局变量来通知子模块一& K1 s) `9 n8 m; c- T% {# g8 \3 z: c notify_module1 = 1; }$ n% j% o. W* x9 ~9 A //module2通知接口& W c+ o' d0 t+ H; O* O# g! i void Update_module2()0 @6 x$ a0 c- }5 l$ A. M {- l! z. ], O2 r' _ //通过设置全局变量来通知子模块一 notify_module2 = 1;+ x5 f6 ?& x0 ]: O }2 O' X% z/ [+ G //module3通知接口 void Update_module3(): a4 t' E' Q5 j. } { //通过设置全局变量来通知子模块一 notify_module3 = 1; } 总结3 h; E b6 ^5 b' n* q 3 H% ?/ Q) m# ]( U; c 这就是c语言中的观察者模式,它可以动态地增加、减少观察对象,解除子模块间的直接耦合,可以很好地预防程序需求发生变化。 但是在实际使用过程中,需要考虑一下开发效率和运行效率问题:
|
微信公众号
手机版