【元件如何正确使用 ?】元件耦合性三大原则 : ADP、SDP、SAP

最少改动 x 最大收益

大纲

  • 简单的任务 ?
  • 神奇的软件
  • 耦合性三大原则
    • ADP , 无环依赖原则
    • SDP , 稳定依赖原则
    • SAP , 稳定抽象原则
  • 领域驱动设计细节
  • 最少改动,最大收益

简单的任务 ?

有一个任务,要调整介面上的表格数据:

  • 客户觉得:「应该很简单」
  • PM 觉得:「半小时搞定」

打开程序

你发现有太多的其他功能,都要使用到这个数据。

这里一改动,就代表了全部都要一起改。
  • 你只觉得: 血压好像有点微微的增高。

功能模组,使用了其他功能

两者建立了依赖关系,这种行为在软件中也会称为:

「耦合」

理想中的目标

高内聚 低耦合
  • 高内聚 : 分类分得越精准越好。
  • 低耦合 : 建立的关系越少越好。

要怎麽达成 ?

耦合性三大原则

可以先帮你实现低耦合的目标。


神奇的软件

「软件」是一种很神奇的东西,看似很自由,可以随意添加新的功能。

如果真的这麽随意

001-1.software

自由的空间,反而会越来越少 (越来越难改动)

依循一些原则,规范开发的方向

001-2.software

看似多了限制,却反而能使得,未来的扩展空间 大大的增加。

软件的特性,也是一种「违反直觉」的力量


「违反直觉」延伸阅读 :


Clean Architecture 无瑕的程序码

整洁的软件设计与架构篇

除了「物件导向的五大设计原则」以外,还有「元件的内聚性原则」与「元件的耦合性原则」。

001-3.principle


先後顺序

实际上的顺序,应该是要:

内聚性先,而耦合性後

因为我个人认为「耦合性」的概念,比较好懂; 原则的部分,也相对容易,应用於开发上。

这次,会先说明耦合性的部分

下一篇章,会接着继续说明内聚性的部分


耦合性三大原则

002.principle

ADP 无环依赖原则

在元件的依赖关系图中不允许出现环

SDP 稳定依赖原则

朝着稳定方向进行依赖

SAP 稳定抽象原则

元件的抽象程度应该与元件的稳定程度一致


ADP , 无环依赖原则

Acyclic Dependencies Principle

在元件的依赖关系图中不允许出现环

从字面上来理解,就是元件在需要其他的元件工作时,之间构成的依赖关系,不可以是环状循环。

003-1.ADP


为什麽 ?

因为依赖於其他元件,目的是为了

「减轻工作量」

别的元件有做过的事情,就不要重新再做一次。

如果元件的依赖会互相参照,代表 :

  • 我的元件调整後,可能会影响到要依赖的元件
  • 依赖的元件被影响後,又有可能回头影响到我的元件

就好像构成了 一个短路循环 ,直到系统**「过载」**


从「减轻工作量」这个角度来看,「循环依赖」更像是一个工作制造机

003-2.ADP

可以源源不绝的,让你有做不完的工作。(不会失业 XD)

SDP , 稳定依赖原则

Stable Dependencies Principle

朝着稳定方向进行依赖

延续无环依赖原则的思路,除了依赖不能造成循环,依赖还是最好有个方向。

004-1.SDP

这个方向的路标,就是「稳定度」


稳定度 ?

也是以「工作量」的多寡来决定

稳定的元件 ?

元件被很多的功能依赖,但本身没有去依赖任何东西

004-2.SDP

  • 不会有外部的元件,影响到它
  • 但它却能够影响到,很多元件

改了它,就代表了巨大的「工作量」

所以这个元件是「稳定」的

不稳定的元件 ?

如果这个元件,没有功能依赖它,但它依赖了很多外部的元件

004-3.SDP

  • 外部的元件更动,它都有可能受到影响
  • 但它本身自行改动 却不会影响到其他元件

修改它的「工作量」非常的低

所以这个元件是「不稳定」的

稳定度计算公式

藉由依赖的关系数量,这里就会有一个的公式,可以计算元件的稳定度

004-4.formula

理想的状况,就是由不稳定的元件,朝向稳定的元件,建立依赖关系


SAP , 稳定抽象原则

Stable Abstractions Principle

元件的抽象程度 应该与元件的稳定程度一致
  • 「稳定」: 在上一个原则 已经知道什麽意思
  • 「抽象」: 则代表的是 只表达「概念」但并不「实作」

005-1.SAP

也就是越重要的元件,应该越只表达纯粹的「概念」


为什麽 ?

稳定的核心元件,虽然稳定,但失去弹性不能扩增,并不是软件设计想要看到的情况。

005-2.SAP


举个例子

餐厅来说,厨师就是稳定的核心元件

005-3.SAP

由厨师本身会煮什麽菜,来决定餐厅会有什麽样的菜单

对於,有点规模的餐听,这并不是一个好的情况


这边的原则,就是将表达**「概念」「菜单」**,当成是最稳定的核心元件

005-4.SAP

如此,就不会受到限制。各个连锁店的厨师,也能根据菜单的菜谱,快速增加新的菜色。


领域驱动设计细节

006.ddd

依据耦合性的原则,领域驱动设计的概念,补充实作部份的三个细节:

  • Domain 层
  • Application 层
  • Infrastructure 层

Domain 层

通常就是系统的核心,所以 Domain 的 Service 通常会宣告成 Interface 的介面

007-1.AppInfoService

名称结尾为 Service


然後,在同目录的 impl/ 资料夹,创建新的类别实作(implements) 该 Service 的介面

007-2.AppInfoServiceImpl

名称结尾为 ServiceImpl

这里是借鉴了 「SAP 稳定抽象原则」,越稳定的元件应该越抽象。

Application 层

负责的是流程作业,相比於 Domain 层变动性是比较大的,虽然说同样有 Service 的类别,但不再创建 Interface 的介面

008.AppInfoAppService

结尾的名称命名 使用 AppService

这里是借鉴了 「SDP 稳定依赖原则」,把容易变动的东西,变得更加容易改动。

Infrastructure 层

提供所有的技术支援,从 DDD 的架构分层可以很明显地看到,它是个自上而下的依赖关系

006.ddd

但对於与 Domain 层的关系,我认为不应该被领域层的元件所依赖

有两个部分可以具体说明:

  • DAO 资料持久化
  • Util 共用工具

第一个 : DAO 资料持久化的部分

DAO , Data Access Object

领域层(Domain Layer)的服务(Service) ,不应该直接 调用 DAO 来完成资料保存的任务

009-1.DAO

而是应该提升一个层级,交由应用层(Application) 来执行这项任务

009-2.DAO

避免资料库的变动会影响到领域层(Domain Layer)的功能


第二个 : Util 工具的使用

util 工具,通常会是各种第三方的框架,封装而来

010-1.util

如果,第三方的框架:

  • 有重大的 bug

  • 有更好的框架可以更换

    调整 util 元件,就会影响到 领域层(Domain Layer) 的功能


这种情况...

不如就由领域层(Domain Layer) 提供 :

  • 抽象方法的类别(abstract)
  • 抽象型别的介面(interface)

然後由,基础设施层来实作功能。

010-2.util

不管最後是使用什麽样的技术实作,只要能够通过,领域层的单元测试,就可以确保系统的正确性。


违反直觉

这部分,可能一时之间无法转换过来,不过这里要表达的就是:

010-3.domain-core

Domain 层 是稳定的核心元件,不应该去依赖其他层级的元件。


最少改动,最大收益

依据上述耦合性的三大原则,有没有发现一件事情:

**「稳定」与「不稳定」**并不是一个「好」与「坏」的关系

而是这个「稳定度」与元件的「特性」是否是相符合。

011.final

最容易变动的「使用者介面层」、「应用层」,就是放在上两层,向下依赖


  • 耦合性的「无环依赖」与「稳定依赖」,基本上已经大部份的符合
  • 领域层的部分,添加了三个细节,让它看起来更加核心,也符合了「稳定抽象的原则」

完美的「设计」?

不敢说是最好的「设计」,但我知道 :

建立关系

不应该是那麽的随心所欲

朝着正确的方向不断精进,也许哪一天也可以触碰到传说中:

「最少改动,最大收益」 

软件开发的天堂之境

012.final


语录

工程师OS

只有资深工程师才有资格跟工程师说:「应该很简单」
	
     -- Gamma Ray Studio

参考资料


<<:  蓝牙威胁(Bluetooth Threats)

>>:  国军放假自动汇整回报网页

予焦啦!参数与环境变数

本节是以 Golang 上游 8854368cb076ea9a2b71c8b3c8f675a8e1...

[资料库] 学习笔记 - 商城交易之产生订单

这次练习的题目是做出商城中产生订单的功能 功能主要需求:产生订单 (目前一笔订单只能买一个品项)、库...

[DAY-07] 强化人才密度 拿出业界最高薪资

从 WFH 的 三个半月多 ( 2021- 05, 06, 07, 08) 再回到 办公室 &am...

[Day-18] 一维阵列小练习

上次学习了基本的阵列语法 这次要来练习简单的阵列使用 都是基本的宣告以及画面显示 主要是练习一维阵列...

Day00:Setting up Environment(环境设定)

全文同步於个人 Docusaurus Blog Foreword 过往前後端沟通,需要仰赖 cli...