zoupaper的个人博客分享 http://blog.sciencenet.cn/u/zoupaper

博文

设计模式与现代C++ 连载(二)

已有 2623 次阅读 2019-3-19 14:37 |系统分类:科研笔记

3 命令模式

命令模式也许是最能发挥bind/function威力的模式,它可以把请求,调用函数等操作打包成对象,然后可以对对象统一管理,方便的实现用户操作的汇总、回退等。《设计模式》这本书里面,是继承抽象的基类,然后使用基类对象实现命令的管理。而采用bind/function更加简单直接,直接把一切可调用物(无论是普通函数、还是Lamda函数,还是类里面的函数)都打包成对象。

网上介绍命令模式一般都是结合GUI程序。我提出使用命令模式的另一个例子。

考虑一个模块,有若干个输入,输出一个信号。也就是说,输入输出是明确的。但是模块内部非常复杂,有许多的子模块。这样的模块应该具有普遍性。比如说,继电保护装置,输入的是电流电压这样的原始测量数据,输出离散信号,可能是动作,或者信号,或者不动作等等。但是继电保护装置内部的结构很复杂,有许多功能子模块。前段时间失事的波音737MAX飞机的下压机头程序,输入的是迎角传感器数据,输出的是飞机飞行姿态指标。内部肯定也有许多子模块。

如果构建子模块的时候,只需要考虑子模块的内部实现,不需要考虑子模块的输入从哪里来,输出往哪里去。这样就可以最大程度的解耦、降低心智负担。至于子模块间的联系,由上层模块负责。这样,模块逻辑的拓扑构建,与各个子模块具体的计算是分离的。我们完全可以运行中动态改变模块的逻辑结构、增减子模块,模块计算可以不受影响。甚至,我们可以搭积木似的,只提供子模块的实现和接口,让非计算机专业的人士轻松构建完整的模块。

要做到上述功能需求,需要把命令(子模块的输入输出)的构建与命令的执行分开,把命令作为专门的对象可以管理(用于构建上层逻辑)。我举一个极其简单的例子,有两个子模块。第一个模块A有两个数字输入,本身执行加法,其输出作为第2个子模块的输入。第2个子模块B执行乘以2的操作,其输出作为整个模块的输出。

下面是代码:

                                               

这是第一个子模块的实现。


这是第二个子模块的实现。


这是整体模块的实现。


最后是使用方法,输出计算结果6.

  值得注意的是,示例程序只能处理单向无环的子模块结构,如果有更复杂的结构,需要升级程序。

5 观察者/中介模式

     观察者模式应用广泛,对于训练初学者的思维很有作用。它体现的是一对多的依赖关系,当一个对象的状态发生更新时,其它对象都得到通知并处理。

   在《设计模式》一书中,由于采用继承技术,观察者和目标其实仍然有编译上的耦合。而采取bind/function技术可以进一步解除这种耦合。

   中介模式也应用广泛,它使用一个中介对象来封装一系列对象交互。使得各对象无需显示互相引用,从而使得耦合松散。

     如果把观察者模式和中介模式结合起来,就形成了进程内部的消息总线,复杂的对象交互可以通过消息的发送和接受来进行。对象与对象间是没有任何耦合的。要注意的是,这里的消息总线和进程间通信的消息总线是不同的东西。

   进程内部的消息总线有许多应用场景,比如说,按照时间序列进行滚动计算,每次计算要提取哪些信息,不同的用户关注点是不同的。可以采用注册消息的形式,某个消息注册不同的行为,例如潮流计算、第21条线路的功率、可靠性指标等,然后每次滚动计算时发送消息就可以了,非常灵活方便,最重要是,没有把用户偏好“写死”在程序里面。

   下面是一个简单的例子代码:


   这是消息类的定义。

   

这是两个具体类,要注意他们与消息类之间没有任何耦合关系!

   

这是消息的注册和发送,非常简单方便。

 

6 策略/状态模式

     策略模式应该是最简单的模式,也是可以初学者带来很大帮助的模式。初学者写程序容易写出大量的if else 语句或者switch case语句,每个分条件内部都写算法实现。如果需要增加新的算法,就需要一点点修改程序,心智负担重且容易出错。策略模式把算法封装起来,可以动态互相切换,而且算法可以独立增加,不用修改其余算法的实现代码。我这里就不展开讲策略模式了,读者可以在网上搜索。

     状态模式与策略模式很相似,但是理解起来要稍微复杂些。它可以在对象内部状态改变时改变行为,看起来像是修改了它的类。状态模式的应用场景也是很广阔的,比如说某个控制装置的状态可以是发信/动作/闭锁/停运等,每个状态都可以互相切换,对外的行为模式不相同。 

  下面举一个例子说明状态模式。假定一个自动售货机,它有两个状态:有货状态和没货状态。对外接口有两个:卖货和补充货。不同的状态下行为是不相同的。在没货状态,就没法卖货了;同时补充货的数量等于容量。在有货状态,可以对外卖货,同时补充货的数量等于容量减去现有的数量。

下面是头文件定义:


Machine类就是售货机,Machine_empty类是没货状态下的行为,Machine_normal类是有货状态下的行为。Machine类包含b_eb_n,这是两个智能指针。

   这是Machine_empty类和Machine_normal类的具体类声明。

   下面是实现的代码:

   

这是行为类的实现代码。


这是售货机的实现代码,注意状态切换是用bind/function实现的。

下面是测试代码:


程序的输出为:

补充货,补充数量:3

售货,剩余数量:2

售货,剩余数量:1

售货,剩余数量:0

已经没有货!

补充货,补充数量:3

售货,剩余数量:2

补充货,补充数量:1

    可以看出,同样的函数Sell,有时候有货,有时候没货。同样的Make_full,补充的货数量也不同。对用户来说,对象的行为发生了改变,但是又不需要关心对象内部到底有多少状态,这样就减少了耦合。

 

 




https://blog.sciencenet.cn/blog-3316223-1168371.html

上一篇:设计模式与现代C++ 连载(一)
下一篇:设计模式与现代C++ 连载(三)
收藏 IP: 218.94.96.*| 热度|

0

该博文允许注册用户评论 请点击登录 评论 (0 个评论)

数据加载中...

Archiver|手机版|科学网 ( 京ICP备07017567号-12 )

GMT+8, 2024-3-29 03:00

Powered by ScienceNet.cn

Copyright © 2007- 中国科学报社

返回顶部