写点什么

命令模式:若只如“初见”

  • 2018-01-03
  • 本文字数:2713 字

    阅读完需:约 9 分钟

似曾相识

最近在 InfoQ 上看到一篇谈论命令模式与 CQRS 架构的译文《From CQS to CQRS》(建议先阅读此文,本文会针对该文的一些观点进行探讨),文章从命令模式谈起,然后提出了命令模式的升级版——命令总线

这个图总给人一种似曾相识的感觉,仔细回想了一下,发觉这不正是Struts2 架构中的核心部分吗?

作为一个基于命令模式的MVC 框架,Struts2 在对于命令的处理上和命令总线的设计如出一辙,它的 ActionInvocationInterceptor对应的正是命令总线里CommandBusDecorator。应该说两者是因为遵循了同样的 OO 设计准则才得到了如此高度的一致,可见优雅的设计都是相似的,模式之所以成为模式是有必然性的。

命令模式“错”了吗?

关于命令模式的详细介绍可以参考四人帮的《设计模式》和《Head First Design Patterns》,本文不做过多赘述。命令模式的核心设计用意是对调用端和执行端进行解耦,这种解耦是非常彻底的,即:在调用端不会出现任何执行端的 API,甚至在Command这个核心抽象上的execute方法上都是不带任何参数的:

复制代码
public interface Command {
public void execute();
}

这使得Command对于调用方而言完全是一个“黑盒”,调用方只知道下达命令,对于它将被如何执行毫不知情,命令的解释与执行是由命令和执行方共同完成的,这符合现实世界中很多事物之间的协作关系,确保了参与其中的各方职责单一,分工明确,否则角色混乱,各种事情搅在一起就会变成一团糟。

《From CQS to CQRS》一文为引出命令总线在介绍命令模式时阐述了它的一个"缺陷":即由于命令模式的“强”封装使得它不能很好地“包裹”数据,也就是命令参数。文章称这些经常变化的命令参数既不能通过execute 方法传递,又不适合作为命令类构造函数的参数,因此作者认为命令模式是“有问题”的,需要进行重构,而重构的结果就是命令总线。

然而文章对命令模式的diss 是站不住脚的,因为即使按照作者推荐的方式将所谓变化的部分(即“数据”)抽离到一个DTO 中,在调用端依然需要实例化它,为其设定各种参数,这些参数天然就是执行业务的前提,无论采用何种设计,作为下达命令的一方“把要干的事情讲明白”是最起码的“份内事”,所以在命令类的构造函数上传递参数和剥离到一个单独的DTO 中包裹数据没有任何本质的区别,后者的做法反而有“从富领域模型向贫血的领域模型开倒车”的嫌疑。

所以命令模式并没有错,命令总线也不是为解决命令模式所谓的“弊端”而来,它实际上是应更大的架构目标和应用场景而产生的。

更大的格局

原生的命令模式在它所适用的场景上表现自然是完美的,这些场景大多数是领域模型的一些“局部”,命令的类型和逻辑都是和业务紧密联系的。而另一方面,人们也认识到命令模式具有广泛的适用性,具备在更高级别的架构模式中扮演核心角色的能力,但是将命令模式提升到更加通用和完备的层面还需要解决以下一些问题:

1. 将命令的“数据”和“逻辑”剥离开,形成通用的“命令”和“命令处理机制”

在原生的命令模式里,每一个具体的命令类都会包含特定的字段和逻辑,通用化处理的第一步就需要把命令的数据和行为剥离开,数据剥离之后可以使用通用的数据结构如 Map 或更加抽象的类型如 Object 来替换,而行为上的通用化处理则要依靠下面几点来实现。

2. 抽象统一的命令处理流程

在一个特定的框架或业务系统里,命令的执行往往都有一定的“套路”,如果想让命令的执行通用化,势必要精心地总结和归纳各种命令在执行上的共性,提炼出一个通用的程序执行的“流程”,这个所谓的“流程”就是服务总线模式中的CommandBus和 Struts2 中的ActionInvocation,统一处理流程可以包含大量丰富的主题,比如日志、事务处理、安全拦截、性能跟踪、数据校验等等。

3. 基于配置的流程定义与组装

但是统一的处理流程并不意味着只能有一种,也不意味着一成不变,为了让流程处理具有广泛的适用性,通过配置的方式去定义和组装命令的处理流程是非常必要的,这样可以让流程变得灵活,可定制,流程中的环节也都是可插拔的,就如同Struts2使用struts.xml去描述interceptors栈和action那样。

4. 提供命令处理的公共基础设施

当统一的“流程”抽象出来之后,需要针对普遍存在的“环节”提供公共实现,例如前文命令总线上示意的LoggingDecoratorValidationDecorator等一系列的装饰器和 Struts2 中的loggervalidation等一系列的Interceptor,这些都会作为命令处理过程中的“公共基础设施”,一环一环地套接起来,让每一个命令逐一经过这些“环节”进行相应的处理。这种工作模式和面向切面编程中的“Around Advice”机制是完全一致的。

5. 给自定义命令处理逻辑留下接口

无论如何,这处理流程上的最后一环必定是留给命令“执行者”的,连同封装好的数据一起,落脚到一个回调的接口上,让命令“执行者”们补上属于它们的应尽之责:业务处理代码,则整个命令处理流程的“闭环”就算大功告成了。

原生的命令模式往往应用在领域模型上,与业务紧密关联,而命令总线的意图则是试图将命令模式提升到架构层面,在整个系统的某些“分层”(layer) 之间建立一种一致的全局的通信模式,从而实现“层间解耦”,例如像 Struts2 那样在 MVC 的视图层与模型层之间组织和传递 Action。为了实现这一目标,势必要对原生的命令模式进行改进,甚至是妥协,比如将命令的数据与行为进行拆分,这确实像是“从富领域模型向贫血的领域模型开倒车” ,但是为了实现更大的架构目标,局部的妥协是必须的,也是值得的。

终极产物

如从一条小溪最终汇入江河大海,命令模式被提升为命令总线之后进而又参与到了 CQRS 架构中,成为组成这一先进架构的核心模式之一,这也可以视为命令模式进化到现在的“终极产物”。CQRS 架构的核心思想是把系统和外界的信息交换进行了读写分离,在数据写入时,通过构建富领域模型进行业务计算,这是领域驱动设计擅长的领域,在这个过程中“命令”是驱动领域模型运转的钥匙。在数据读取时,CQRS 会绕过领域模型直接从持久层提取数据,这有助于提升性能,同时减轻领域模型的压力。

但是 CQRS 已经不再是本文关注的重点了,因为 CQRS 直接复用了命令总线,没有做其他的提升,本文写作的主要目的是想回顾命令模式从起源到终极产物的演化历程,阐述这些演化背后的真正用意以及实现这些目标的宝贵设计思想。

关于作者:耿立超,架构师,CSDN 博客专家,博客 http://blog.csdn.net/bluishglc 已从事多年大数据领域的研发工作,对企业级应用架构、SaaS、分布式存储和领域驱动设计有丰富的实践经验,喜欢摄影和旅行。


感谢徐川对本文的审校。

给InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家通过新浪微博( @InfoQ @丁晓昀),微信(微信号: InfoQChina )关注我们。

2018-01-03 18:002669

评论

发布
暂无评论
发现更多内容

week2-作业一

未来已来

第六周学习心得

熊桂平

极客大学架构师训练营

架构第六周总结

Geek_Gu

week-6-part1 CAP 原理

陈龙

在 iOS App 中显示 Build 时间和 git 分支名、commit 哈希

疯清扬

ios 编译时间 git version build time 编译日期

简述CAP原理

orchid9

6.2分布式关系数据库(下)

张荣召

Dynatrace抓取系统中的任何方法Method的参数值

东风微鸣

APM Dynatrace

2 期架构师训练营 - 框架设计

云飞扬

极客大学架构师训练营

6.3CAP原理与NoSQL数据库架构

张荣召

第六周总结

orchid9

第二周课后练习

刘洋

极客大学架构师训练营

第二周作业

伊灵

week-6-part2 学习总结

陈龙

架构二期第二周总结

supersky6

ARTS打卡 第22周

引花眠

微服务 ARTS 打卡计划 springboot

架构师训练营2期第二周总结

架构师训练营 1 期 - week06 - 总结

lucian

极客大学架构师训练营

第6周作业

paul

架构师训练营第六周学习总结

文智

极客大学架构师训练营

11/1-第二周-总结

张冬冬

心得

2 期架构师训练营 - 第二周学习总结

云飞扬

极客大学架构师训练营

6.4Zookeeper与分布一致性架构

张荣召

非HTTP应用或批处理应用如何进行全链路监控

东风微鸣

全链路监控 非HTTP应用

架构师训练营第 6 周课后练习

叶纪想

极客大学架构师训练营

架构师训练营第六周作业

脸不大

技术选型(二)

wing

极客大学架构师训练营

架構師訓練營第 1 期 - 第 06 周作業

Panda

架構師訓練營第 1 期

架构师训练营1期-week06-作业

lucian

极客大学架构师训练营

架构师二期第二周作业

supersky6

作业

架构师训练营第六周作业

文智

极客大学架构师训练营

命令模式:若只如“初见”_架构_耿立超_InfoQ精选文章