关于分布式:分布式系统实践解读丨详解高内聚低耦合

50次阅读

共计 2987 个字符,预计需要花费 8 分钟才能阅读完成。

摘要: 做好高内聚低耦合,思路也很简略:定职责、做归类、划边界。

上面的这个场景你可能会感觉很相熟(Z 哥我又要出演了):

Z 哥:@All 兄弟姐妹们,这次我这边有个需要须要给「商品上架」减少一道审核,会影响到大家和我交互的接口。大家抽空配合改一下,今天一起更新个版本。

小 Y: 哥,我这几天很忙啊,昨天刚配合老王改过促销!

小 X: 行~当所有已成习惯。

作为被告诉人,如果在你的事实工作中也产生了相似事件,我置信哪怕嘴上不说,心里也会有不少想法和埋怨:“md,改的是你,我也要公布,好冤啊!”。

这个问题的根本原因就是多个我的项目之间的耦合度过于重大。

越大型的我的项目越容易陷入到这个昭潭中,难以自拔。

而解决问题的形式就是进行更正当的分层,并且继续保障分层的合理性。

一提到分层,必然离不开 6 个字「高内聚」和「低耦合」。

什么是高内聚低耦合

在 z 哥之前的文章中有屡次提到, 分布式系统的实质就是「分治」和「冗余」

其中, 分治就是“合成 -> 治理 -> 归并”的三部曲 。「高内聚」、「低耦合」的概念就来源于此。

须要留神的是,当你在做「合成」这个操作的时候,务必要关注每一次的「合成」是否满足一个最重要的条件: 不同分支上的子问题,不能相互依赖,须要各自独立

因为一旦蕴含了依赖关系,子问题和父问题之间就失去了能够被「归并」的意义。

比方,一个「问题 Z」被分解成了两个子问题,「子问题 A」和「子问题 B」。然而,解问题 A 依赖于问题 B 的答案,解问题 B 又依赖于问题 A 的答案。这不就等于没有合成吗?

题外话 :这里的“如何更正当的合成问题”这个思路也能够用到你的生存和工作中的任何问题上。

所以,当你在做「合成」的时候,须要有一些很好的着力点去切入。

这个着力点就是后面提到的「耦合度」和「内聚度」,两者是一个此消彼长的关系。

越合乎高内聚低耦合这个规范,程序的保护老本就越低。为什么呢?因为依赖越小,各自的变更对其余关联方的影响就越小。

所以,「高内聚」和「低耦合」是咱们该当继续一直谋求的指标。

题外话 :耦合度,指的是软件模块之间相互依赖的水平。比方,每次调用办法 A 之后都须要同步调用办法 B,那么此时办法 A 和 B 间的耦合度是高的。

内聚度,指的是模块内的元素具备的共同点的类似水平。比方,一个类中的多个办法有很多的共同之处,都是做领取相干的解决,那么这个类的内聚度是高的。

怎么做好高内聚低耦合

做好高内聚低耦合,思路也很简略: 定职责、做归类、划边界

首先,定职责就是定义每一个子系统、每一个模块、甚至每一个 class 和每一个 function 的职责。

比方,在子系统或者模块层面能够这样。

又比方,在 class 或者 function 层面能够这样。

我想这点大家平时都会无意识的去做。

做好了职责定义后,内聚性就会有很大的晋升,同时也进步了代码 / 程序的复用水平。

至此,咱们才谈得上「繁多职责(SRP)」这种设计准则的使用。

其次,做归类。梳理不同模块之间的依赖关系。

像下面提到的案例 1 能够归类为 3 层:

  1. 根底层:商品根底服务、会员根底服务、促销根底服务
  2. 聚合层:购物车服务、商品详情服务、登陆服务
  3. 接入层:快闪店 API、综合商城 API

案例 2 也能够归类为 3 层:

  1. 数据拜访层:拜访会员表数据、拜访会员积分表数据、拜访会员等级表数据
  2. 业务逻辑层:会员登陆逻辑、会员应用积分逻辑、会员降级逻辑
  3. 应用层:接管用户输出的账户明码、接管用户输出的应用积分数、接管用户的付款信息

最初就是划边界。好不容易梳理分明,为了防止轻易被再次毁坏,所以须要设立好正当清晰的边界。

否则你想的是这样参差。

理论会缓缓变成这样凌乱。

那么应该怎么划边界呢?

class 和 function 级别。这个层面能够通过 codereview 或者动态代码检测工具来进行,能够关注的点比方:

1. 调用某些 class 必须通过 interface 而不是 implement

2. 拜访会员表数据的 class 中不能存在拜访商品数据的 function

模块级别。 能够抉择以下计划:

1. 给每一种类型的 class 调配不同 project,打包到各自的 dll(jar)中

2. 每次代码 push 上来的时候检测其中的依赖是否有超出规定的依赖。例如,不能逆向依赖(检测 dal 是否蕴含 bll);不能在根底层做聚合业务(检测商品根底服务是否蕴含其余根底服务的 dll(jar))。

零碎级别。 及时辨认子系统之间的调用是否合乎预期,能够通过接入一个调用链跟踪零碎(如,zipkin)来剖析申请链路是否非法。

让边界更清晰、稳固的最佳实际

很多时候不同的模块或者子系统会被调配到不同的小组中负责,所以 z 哥再分享几个最佳实际给你。它能够让零碎之间的沟通更稳固。

首先是: 模块对外裸露的接口局部,数据类型的抉择上尽量做到宽进严出 。比方,应用 long 代替 byte 之类的数据类型;应用弱类型代替强类型等等。

举个「宽进严出」的例子:

// 应用 long 代替 byte 之类的数据类型。void Add(long param1, long param2){if(param1 <1000&& param2 < 1000){  // 先接管进来,到外面再做逻辑校验。//do something...
    }
    else{//do something...}
}

其次是: 写操作接口,接管参数尽可能少;读操作接口,返回参数尽可能多

为什么呢?因为很多时候,写操作的背地会存在一个潜在预期,是「精确」。

准确度和可信度有着很大的分割,只有更多的逻辑解决在本人掌控范畴内进行能力越具备「可信度」(当然是职责范畴内的逻辑,而不是让商品服务去计算促销的逻辑)。反之,上游零碎一个 bug 就会牵连到你的零碎中。

而读操作背地的潜在预期是:「满足」。你得提供给我满足我以后须要的数据,否则我的工作无奈发展。

然而呢,在不同期间,客户端所须要的数据可能会发生变化,你无奈预测。所以呢,不要悭吝,返回参数尽可能多,用哪些,用不必是客户端的事。

还能够做的更好的一些,就是,在能够满足的根底上反对按需获取。客户端须要返回哪些字段本人通过参数传过来,如此一来还能避免浪费资源做无用的数据传输。

题外话 :对外露出的接口设计,能够应用 http + json 这种跨平台 + 弱类型的技术组合,可具备更好的灵活性。

实际上,一个程序大多数状况下,在某些时刻是客户端,又在某些时刻是服务端。站在一个残缺程序的角度来提炼参数设计的思路就是:“吃”的要少,“产出”的要多

题外话: 有一些设计准则能够扩大浏览一下。

繁多职责准则 SRP(Single Responsibility Principle)

凋谢关闭准则 OCP(Open-Close Principle)

里式替换准则 LSP(the Liskov Substitution Principle LSP)

依赖倒置准则 DIP(the Dependency Inversion Principle DIP)

接口拆散准则 ISP(the Interface Segregation Principle ISP)

总结

本文 z 哥带你梳理了一下「高内聚低耦合」的实质(来自于哪,意义是什么),并且分享了一些该怎么做的思路。

能够看到「高内聚」、「低耦合」其实没有这个名字那么高端。哪怕你当初正在工作的我的项目是一个单体利用,也能够在 class 和 function 的设计中领会到「高内聚」、「低耦合」的奥秘。

来来来,接下去马上开始在我的项目中「刻意练习」起来吧~

点击关注,第一工夫理解华为云陈腐技术~

正文完
 0