架构整洁之道导读(四)第25章层次与边界-图 25.3 疑惑澄清

最近收到读者反馈,《架构整洁之道》第 25 章“层次与边界”中,图 25.3 和解释这张图的一段文字的描述让人很费解。

图25.3 修正后的设计图

如果我们进一步查看 GameRules 内部,就会发现 GameRules 组件的代码中使用的 Boundary 多态接口是由 Language 组件来实现的;同时还会发现 Language 组件使用的 Boundary 多态接口由 GameRules 代码实现。

读者的疑惑是 GameRules 和 Language 分别定义接口让对方实现,那么这两个组件不就形成双向依赖了吗?

我们仅凭直觉就知道双向依赖肯定是错的,那么这句话到底该作何解释?为了排除翻译出错的可能性,我仔细比照了原文,确定了译文并不损失原意。

If we were to look inside GameRules, we would find polymorphic Boundary interfaces used by the code inside GameRules and implemented by the code inside the Language component. We would also find polymorphic Boundary interfaces used by Language and implemented by code inside GameRules.

我回头翻阅了当时技术审校的文章,遗憾地发现自己确实疏漏了对此处的解释,所以我重新翻阅了书中和边界(Boundary)相关的章节,比如第 22 章的“整洁架构”和第 24 章的“不完全边界”,给出一个牵强的解释:我认为 Bob 大叔在这里提到的 Boundary 多态接口(polymorphic Boundary interfaces)指的是系统在构建完全边界时需要的 inputBoundary 和 outputBoundary 的。但是不管如何,也不可能出现他说的低级别的组件(此处的 Language)定义接口让高级别的组件(GameRules)去实现的情况。

为了解决读者心中的疑惑,我写了封邮件给 Bob 大叔。这封邮件的内容重点在询问 GameRules 为什么会有对 Language 的依赖。Bob 大叔的回复得很迅速,内容如下:

The secret here is that the polymorphic interfaces used by Language and implemented by Game Rules are contained within Game Rules. This keeps the arrows pointing in the same direction.

他强调 Language 使用的多态接口其实是定义在 GameRules 当中的,所以依赖方向依然是从 Language 指向 GameRules 的。看到这样的回复,我还是有些迷惑,不过认真思考了一段时间,我大概知道这里的误解到底是什么了。

其实答案就隐藏在第 22 章“整洁架构”里图22.2 一个基于 Web 的、使用数据库的常见 Java 程序

结合这张图,我们不难总结出 Language和 GameRules 的依赖关系,边界和高层次的接口定义,这里面最需要澄清的点就是“使用”并不意味着“定义”,而只是引用。为了确保我的理解正确,我引用了原文并逐行提出我的问题,然后画了一些图,发送一封确认邮件。Bob 大叔很仔细地回复了我的问题。我把邮件原文列在底下。其中,Q 代表我的问题,Answer 是 Bob 大叔的回答。

"The diagram in Figure 25.3 has gotten a little complicated, but should contain no surprises. The dashed outlines indicate abstract components that define an API that is implemented by the components above or below them. For example, the Language API is implemented by English and Spanish."

Q1: Language components define interfaces that should be implemented by its derivates like English and Spanish that are below the Language component. So why you emphasise ABOVE them?

Answer: In Figure 25.3 you’ll see SMS and Console above Text Delivery. Text Delivery does not actually exist as an independent module. It is an abstraction, or a set of conventions, that both SMS and Console adhere to. English and Spanish appear below Language. Again, Language does not actually exist as a module. English and Spanish simply adhere to the conventions that we call Language.

"GameRules communicates with Language through an API that GameRules defines and Language implements. Language communicates with TextDelivery using an API that Language defines but TextDelivery implements. The API is defined and owned by the user, rather than by the implementer."

Q2: GameRules defines API (interface) that Language component implements, does it mean GameRules and Language are separated components?

Answer: Not quite. English and Spanish are separate from Game Rules and both implement the API defined in Game Rules. The Language abstraction is draw there to denote that English and Spanish follow the conventions that Game Rule and Text Delivery require.

"If we were to look inside GameRules, we would find polymorphic Boundary interfaces used by the code inside GameRules and implemented by the code inside the Language component. We would also find polymorphic Boundary interfaces used by Language and implemented by code inside GameRules."

Q3: In Chapter 22, there is figure 22.2 a typical scenario for a web-based Java system utilising a database.

figure 22.2 a typical scenario for a web-based Java system utilising a database

Here, I mark connections to explain my understanding for "used by" and "implemented by". So "used by" is not meaning "defined by", right?

Answer: Right.

If my understanding is correct, how about the diagram I draw as follow? does it explain the secret you mentioned above?

Boundary interfaces

"If we were to look inside of Language, we would find the same thing: Polymorphic Boundary interfaces implemented by the code inside TextDelivery, and polymorphic Boundary interfaces used by TextDelivery and implemented by Language."

"In each case, the API defined by those Boundary interfaces is owned by the upstream component.”

Q4: In GameRules and Language components context, upstream component means the GameRules? In Language and TextDelivery components context, upstream means the Language?

Answer: Yes. Upstream means “higher level".

当我把邮件完整地转发给读者后,他表示“我看了 Bob 大叔的回答,感觉对依赖反转的认识又深了一层。不,应该说看了你的回答(商业互吹)。抽象出的接口,我从来没想过接口的归属问题,这么看来就合理了。”

小结

以后我们在设计组件时一定要关心边界和接口定义的归属。它代表着依赖反转原则在更大的架构层面上的运用。

本书的读者朋友们,如果有类似的问题,作为中文版的技术审校者,我是非常欢迎邮件交流的,我的个人邮箱:qianyan.lambda@gmail.com

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏码匠的流水账

聊聊NacosNamingService的getServicesOfServer

本文主要研究一下NacosNamingService的getServicesOfServer

7700
来自专栏机器视觉CV

OpenCV 实现多张图像拼接

OpenCV中从2.4.x版本之后多出来一个新的模型 图像拼接,该模块通过简单的高级API设置,可以获得比较好的图像拼接效果,OpenCV官方提供了一个高度集成...

23220
来自专栏DotNet Core圈圈

在 ASP.NET Core 中使用 AI 驱动的授权策略限制站点访问

ASP.NET Core 引入声明授权机制,该机制接受自定义策略来限制对应用程序或部分应用程序的访问,具体取决于经过身份验证的用户的特定授权属性。在上一篇文章中...

14020
来自专栏IT大咖说

为什么说要用DDD替代CRUD来设计API

来自亚马逊的高级工程师 James Hood 以简单明了的例子说明了为什么要用 DDD 替代 CRUD 来设计 REST API。他提到“DDD 与 REST ...

9720
来自专栏直播、短视频那些事儿

在一对一直播软件开发过程中,需要用到哪些前处理技术(一)

在直播中做好对音视频的前处理,是保证用户基本体验的第一步,因为它牵扯到很多关于美颜和声音的优化问题,用户的感官体验是否达标,跟这个环节有很大联系。那么在一对一直...

11800
来自专栏腾讯技术工程官方号的专栏

说说JS中的沙箱

? 沙箱,即sandbox,顾名思义,就是让你的程序跑在一个隔离的环境下,不对外界的其他程序造成影响,通过创建类似沙盒的独立作业环境,在其内部运行的程序并不能...

24830
来自专栏贾志刚-OpenCV学堂

可微分的「OpenCV」:这是基于PyTorch的可微计算机视觉库

目前最经典的图像处理库差不多就是 OpenCV 了,它从最经典的图像算法到非常前沿的 DL 预训练模型囊括了 CV 的很多方面。但现在有一个问题,OpenCV ...

9930
来自专栏汇智网教程

Geth GraphQL使用说明

Geth V1.9.x增加了GraphQL的支持,开发者可以在经典的JSON RPC API和GraphQL API之间根据自己的去中心化应用具体需求进行选择。...

10700
来自专栏机器学习算法与Python学习

9月份Github上最热门的Python开源项目

https://github.com/Yorko/mlcourse.ai Star 6008

12950
来自专栏汇智网教程

plotly可视化快速教程

Plotly是新一代的Python数据可视化开发库,它提供了完善的交互能力和灵活的绘制选项。本文将介绍新手如何安装plotly并编写第一个plotly绘图程序,...

29800

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励