架构规划准则之我见(二):SOLID 准则

云栖号资讯:【点击检查更多职业资讯】
在这儿您能够找到不同职业的第一手的上云资讯,还在等什么,快来!

SOLID 准则,据 WikiPedia 所说,是由 Robert C. Martin 总结的面向目标规划准则。这个姓名其实是以下五个准则的首字母简写:

  • Single responsibility principle;
  • Open/closed principle;
  • Liskov substitution principle;
  • Interface segregation principle;
  • Dependency inversion principle。

“Single responsibility principle”

这句话翻译成中文是“单一责任准则”。这是一句缺少主语的话,揣度应该是指规划师所规划的体系吧。所以弥补完好后,整句话的意思应该是:“规划师所规划的方针体系,其责任应该是单一的”。

怎样断定“责任”是否“单一”?

断定“责任单一”的规范是什么难以答复,只能经过作者的文章进一步剖析,测验了解作者原意。
这个准则也并非 SOLID 准则作者原创,据作者原文所说:“This principle was described in the work of Tom DeMarco and Meilir Page-Jones . They called it cohesion”,本来这个准则来源于 Tom DeMarco 和 Meilir Page-Jones 两位长辈的作业,本来叫做“Cohesion”,也便是“内聚”。作者对“内聚”给出的解说是:“A class should have only one reason to change”。下文依据作者所给出的比方,来进一步了解作者的目的。

文章最初以一个保龄球游戏的编程规划来讨论这一准则。本来 Game 类有两个责任:一、担任盯梢当时帧,相当于打球;二、担任核算分数。作者以为,假如把这两个责任放在同一个类中,会引起耦合,因而要对 Game 作架构拆分,把这两个责任别离拆分给两个不同的类,并给出了拆分的理由:“Because each responsibility is an axis of change”,意思是“由于每个责任都是一个改变的维度”。猜测作者想表达的是,由于这两个责任是相互正交的维度,分拆开后,能够防止它们相互影响的意思。

这儿其实有两个问题:
首要,两个责任放在同一个类中,并不代表会发作耦合。

耦合的意思是当一个责任内部发作改变时,会影响到别的一个责任的正常履行。假定把两个责任的代码糅合在一同,构成一个大的代码块,这当然是耦合的,此刻修正任何一个责任都要当心,牵一发而动全身。

可是咱们能够把这两个责任放在两个不同的办法中,比方拆分红 Game.trackFrame(), Game.calcScore() 两个办法后,在修正其间一个责任时,只需输入输出的参数不发作改变,也并不会发作耦合。也便是说,要处理耦合这一问题,并非只需“拆分红两个不同的类”这一个处理方案,在同一个类中拆分红两个办法也能够处理,由于拆分红办法是拆分红类的条件。是否需求拆分红类,还需求有其他方面的考虑,解耦这一理由还不行充沛,此处就不具体打开。

其次,许多人都疏忽了为何两个责任能够被拆分隔。

咱们需求回到现实生活来剖析保龄球游戏的中心生命周期。

在现实生活中打保龄球时,的确有算分这一环节。在每一次打球完毕时, 机器会主动给出分数。当然,在前期没有机器时,这个分数肯定是由打球人自己来算的。为什么后来能够拆分出来交给机器来算呢?由于算分活动有必要等候打球完毕才干进行,打球与算分二者在履行时刻上是归于彻底不会发作穿插的两个接连动作,且打球的成果作为算分的输入,所以两个动作本来便是没有耦合的,能够拆分隔,成为保龄球游戏生命周期中的两个相续活动。

这两个活动哪一个才是中心生命周期活动呢?能够看到,人们去保龄球馆是为了亲自体会打球,而不是为了体会得分。并且即便没有算分规矩,人们也 能够玩的很高兴,但假如没有打球的体会,只需算分规矩,那么这个游戏也就不成立了。所以,这个游戏的中心生命周期是打球,而非算分。算分只是在打球完毕后对成果的核算,归于非中心生命周,因而分数核算规矩代码能够从打球代码中拆分出来,以保龄球游戏所发作的成果作为算分的输入来推进履行,构成树状结构。

而在拆分后,Game 的本来功用并没发作任何改变,只不过将其间一个过程的完结代码别离出去了算了,然后经过办法调用,以直接获取成果的办法整合回归,仍是同一个全体,没有发作改变。这一做法,使得 Game 能够愈加专心于其自身的责任,分数核算自身也能愈加专心,各自被修正时也能够互不影响。

所以,二者能够拆分隔,并非“Because each responsibility is an axis of change”,而是由于其间存在非中心生命周期活动。并且拆分也并不仅限于拆分红类,首要应该能拆分红办法,这是拆分为类的条件。

“单一”与“内聚”

再从这个比方来剖析“单一”的意义,的确仍是叫“内聚”比较好。

从内聚的视点来看,在打球和算分两个办法拆分隔后,trackFrame() 与 calcScore() 各自都专心于自身的事务,不受对方的影响,因而二者都是内聚的,自身都是完好的,只需给出输入参数就能够独立回来输出成果。并且 Game 这个类完好包括了保龄球自身的事务,其自身也是内聚的。

可是一旦改成“单一责任”,意思就发作了改变,着重点变成了“单一”。其后文在具体解说时,又把表述从“an axis of change”改成“one reason to change”, 意思进一步发作了改变:“an axis of change”指的是一个维度,而“one reason to change”指的是一个理由。二种表述差异很大,彻底误解了“内聚”的原意,难怪会有很大的争议。

别的怎样才干算“责任单一”呢?这是没有确认规范的,需求相关于某个一个参考点才干确认是否单一。比方 Game 包括打球和算分两个过程,莫非 Game 的责任就不“单一”了吗?不是的。保龄球游戏需求打球和算分两个过程,以组成一个“单一”的运动,放在一同正是为“单一”运动而服务的,这样做并不能说不“单一”。只需把比照的目标改为打球和算分时,才干够说 Game 的责任不单一。可是打球和算分自身便是从 Game 中拆分出来的,怎样能够拿全体相对其拆分出来的部分来比“单一”呢?这不合理。假如真的这么去比,即便把打球和算分二者拆分隔后,算分的责任就“单一”了吗?也不是的,算分也能够拆分为许多不同的规矩,在规矩的层面看,算分的责任也并不“单一”,还需求再拆分!依照这个“单一责任”分拆下去,永久没有止境,堕入死循环。

所以“单一”是一个相对的词语,有必要要看针对什么来说是“单一”的,不能独自来看。也不能由于一个工作分为两个过程,就说这个工作不“单一”,由于这两个过程所组成的是同一个工作,是单一的。而把这两个过程拆分隔后由两个人来别离履行,关于这两个人来说,各自的责任仍然是单一的,可是不能因而而否定二者所组成的本来那个工作不“单一”。正由于这两个人各自“单一”职 责的完结,组成了本来的那个“单一”的工作。

回过头来,假如读者了解“内聚”,站在“内聚”的视点来看“单一责任”准则, 来了解作者的“A class should have only one reason to change”这个解说,就能够秒懂作者只不过是想表达“内聚”算了。因而,读者千万不要真的从“单一责任”的视点去了解这个准则,会很简单发作误解,作者不过是想经过这一准则来表述作者所了解的“内聚”意义算了。
把握”内聚“,才是底子!

【云栖号在线讲堂】每天都有产品技能专家共享!
课程地址:https://yqh.aliyun.com/zhibo

当即参加社群,与专家面对面,及时了解课程最新动态!
【云栖号在线讲堂 社群】https://c.tb.cn/F3.Z8gvnK

原文发布时刻:2020-05-08
本文作者:王概凯
本文来自:“InfoQ”,了解相关信息能够重视“InfoQ”