分布式系统关注点——「高内聚低耦合」详解

若是这是第二次看到个人文章,欢迎点击文末连接订阅个人我的公众号(跨界架构师)哟~

本文长度为3012字,建议阅读8分钟。html

坚持原创,每一篇都是用心之做~程序员


下面的这个场景你可能会以为很熟悉(Z哥我又要出演了):json

Z哥:@All 兄弟姐妹们,此次我这边有个需求须要给「商品上架」增长一道审核,会影响到你们和我交互的接口。你们抽空配合改一下,明天一块儿更新个版本。微信

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

小X:行~当一切已成习惯。less

做为被通知人,若是在你的现实工做中也发生了相似事件,我相信哪怕嘴上不说,内心也会有很多想法和抱怨:“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的设计中体会到「高内聚」、「低耦合」的奥妙。


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



「易伸缩」篇的相关文章:



做者:Zachary

出处:www.cnblogs.com/Zachary-Fan…


若是你喜欢这篇文章,能够点一下右下角的「大拇指」哦~。

这样能够给我一点反馈。: )

谢谢你的举手之劳。


▶关于做者:张帆(Zachary,我的微信号:Zachary-ZF)。坚持用心打磨每一篇高质量原创。本文首发于公众号:「跨界架构师」(ID:Zachary_ZF)。<-- 点击后阅读热门文章

按期发表原创内容:架构设计丨分布式系统丨产品丨运营丨一些思考。

若是你是初级程序员,想提高但不知道如何下手。又或者作程序员多年,陷入了一些瓶颈想拓宽一下视野。欢迎关注个人公众号「跨界架构师」,回复「技术」,送你一份我长期收集和整理的思惟导图。

若是你是运营,面对不断变化的市场一筹莫展。又或者想了解主流的运营策略,以丰富本身的“仓库”。欢迎关注个人公众号「跨界架构师」,回复「运营」,送你一份我长期收集和整理的思惟导图。

相关文章
相关标签/搜索