|
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_e和b_n,这是两个智能指针。
这是Machine_empty类和Machine_normal类的具体类声明。
下面是实现的代码:
这是行为类的实现代码。
这是售货机的实现代码,注意状态切换是用bind/function实现的。
下面是测试代码:
程序的输出为:
补充货,补充数量:3
售货,剩余数量:2
售货,剩余数量:1
售货,剩余数量:0
已经没有货!
补充货,补充数量:3
售货,剩余数量:2
补充货,补充数量:1
可以看出,同样的函数Sell,有时候有货,有时候没货。同样的Make_full,补充的货数量也不同。对用户来说,对象的行为发生了改变,但是又不需要关心对象内部到底有多少状态,这样就减少了耦合。
Archiver|手机版|科学网 ( 京ICP备07017567号-12 )
GMT+8, 2024-12-22 14:19
Powered by ScienceNet.cn
Copyright © 2007- 中国科学报社