【AICon】探索RAG 技术在实际应用中遇到的挑战及应对策略!AICon精华内容已上线73%>>> 了解详情
写点什么

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

  • 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:002538

评论

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

NFT铸造交易平台开发详情

开发微hkkf5566

什么是XR扩展现实,XR云串流平台有哪些

3DCAT实时渲染

XR 云XR

腾讯云的一场硬仗

ToB行业头条

墨天轮沙龙 | 清华乔嘉林:Apache IoTDB,源于清华,建设开源生态之路

墨天轮

数据库 国产数据库 apache 社区 Apache IoTDB

“阿里爸爸”最新Java面试指南,基础+框架+数据库+系统设计+算法

Java全栈架构师

Java spring 程序员 面试 架构设计

GameFi链游系统开发NFT技术

薇電13242772558

NFT gamefi

做自助洗车合伙人要投入多少

共享电单车厂家

自助洗车加盟 自助洗车合伙人

边缘计算平台如何助力物联网发展

3DCAT实时渲染

边缘计算

ABAP-发布Restful服务

桥下本有油菜花

abap

1 分钟 Serverless 搭建你的首个个人网站(完成就送猫超卡)

阿里巴巴云原生

阿里云 Serverless 云原生 网站

云流化技术在汽车行业中的应用

3DCAT实时渲染

XR 云流化

实时渲染和预渲染有什么区别

3DCAT实时渲染

渲染 实时渲染

CloudXR如何推动XR的未来发展

3DCAT实时渲染

CLOUDXR

云化XR,如何助力产业升级

3DCAT实时渲染

XR

小程序容器与物联网结合的方式

Geek_99967b

小程序 物联网

CODING 正式入驻腾讯会议应用市场!

CODING DevOps

Hologres共享集群助力淘宝订阅极致精细化运营

阿里云大数据AI技术

sql 大数据 分布式计算 存储 数据可视化

更多龙蜥自研特性!生产可用的 Anolis OS 8.6 正式发布

OpenAnolis小助手

开源 操作系统 龙蜥社区 Anolis OS 8.6 版本发布

wallys/WiFi 6 (802.11ax) 4×4 MU-MIMO 2.4GHz QCN9074 Single Band Wireless Module

wallys-wifi6

如何成为一名共享自助洗车合伙人

共享电单车厂家

共享自助洗车 自助洗车加盟 自助洗车合伙人

“低代码”在企业数字化转型中扮演着什么角色?

优秀

低代码 数字化

知名互联网房屋租赁服务公司物联网关键业务迁移上云实践

EMQ映云科技

物联网 IoT 云服务 emqx 6月月更

流批一体在京东的探索与实践

Apache Flink

大数据 flink 编程 流计算 实时计算

先写API文档还是先写代码?

Liam

Java 前端 Postman 后端开发 后端技术

NFT挖矿游GameFi链游系统开发搭建

薇電13242772558

智能合约 NFT

大学生研究生毕业找工作,该选择哪个方向?

C++后台开发

后端开发 应届生 C++后台开发 研究生 C++开发

日均 6000+ 实例,TB 级数据流量,Apache DolphinScheduler 如何做联通医疗大数据平台的“顶梁柱”?

Apache DolphinScheduler

Apache 大数据 开源 Apache DolphinScheduler

wallys/WiFi 6 (802.11ax) 4×4 MU-MIMO 5GHz QCN9074 Single Band Wireless Module

wallys-wifi6

LeaRun.Java可视化拖拽编辑的BI大屏

力软低代码开发平台

【合集- 行业解决方案】如何搭建高性能的数据加速与数据编排平台

Alluxio

人工智能 互联网 金融 科技 电信

小程序容器技术,促进园区运营效率提升

Speedoooo

智慧园区 小程序容器 园区运营

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