其中正交四原则是一种与范式,语言无关的设计原则。为了解决在模块化的过程中,如何让软件在长期范围内更容易应对变化。包括:
消除重复(被动)。体现了低耦合
分离关注点(主动)。也叫分离不同方向变化,是整个模块化思想的延伸,也就是单一职责和组合复用的表现
缩小依赖范围(主动)。体现高内聚,迪米特法则的表现
依赖与稳定(主动)。耦合点的体现,依赖稳定的接口;是接口分离原则换了一个说法
消除重复:不同代码元素对同一知识进行多次描述,无论描述方式是否一致。1 定义宏消除; 2.封装函数消除;
参数型重复:函数算法一样,处理数据不同:把不同的数据参数化;
调用性重复:2个函数的重复内容完全相同,可以将重复的部分提取为函数F,然后原函数对各自部分直接调用。
回调性重复:重复部分完全相同,可以将重复的部分提取为函数,然后选择调用原函数中的某个。
1 重复:DRY do not repeat yourself, once and only once, singular responsilibity principle
1分离关注点:1. 重复的部分;2.差异的部分;3 规则对比。
分离不同变化方向,目标在于提高内聚度。因为多个变化方向,意味着一个模块存在多重职责。将不同的变化方向进行分离,也意味着各个变化方向职责的单一化。
如果我们足够细心,会发现策略消除重复和分离不同变化方向是两个高度相似和关联的策略:
它们都是关注于如何对原有模块进行拆分,以提高系统的内聚性。(虽然同时也往往伴随着耦合度的降低,但这些耦合度的降低都发生在别处,并未触及该如何定义API以降低客户与API之间耦合度)。
另外,如果两个模块有部分代码是重复的,往往意味着不同变化方向。
尽管如此,我们依然需要两个不同的策略。这是因为:变化方向 并不总是以重复代码的形式出现的(其典型症状是散弹式修改,或者if-else、switch-case、模式匹配);尽管其背后往往存在一个以重复代码形式表现的等价形式(这也是为何copy-paste-modify如此流行的原因)。
2 缩小依赖范围:层次分明,减少互相之间的依赖。依赖点包含尽量少的知识。
高内聚,不应该强迫依赖方依赖它不需要的东西。
而具体到策略缩小依赖范围,它强调:
API 应包含尽可能少的知识。因为任何一项知识的变化都会导致双方的变化;
API 也应该高内聚,而不应该强迫API的客户依赖它不需要的东西。
3 向着稳定的方向依赖:1.依赖点越稳定,依赖方受依赖点变化影响概率越小。
从业务的角度,对需求建模和抽象。
4 我们得到最后一个策略:向着稳定的方向依赖。
那么,究竟什么样的API更倾向于稳定?不难知道,站在What,而不是How的角度;即
站在需求的角度,而不是实现方式的角度定义API,会让其更加稳定。
而需求的提出方,一定是客户端,而不是实现侧。这就意味着,我们在定义接口时,应该站在客户的角度,思考用户的本质需要,由此来定义API。而不是站在技术实现的方便程度角度来思考API定义。
一旦人们开始进行进行模块化拆分,就必须解决如下两个问题:
究竟软件模块该怎样划分才是合理的?
将一个大单元划分为多个小单元之后,它们之间必然要通过衔接点进行合作。如果我们把这些衔接点看作API,那么问题就变为:怎样定义API才是合理的?
内聚与耦合
每个读过基础软件工程教程的人都知道:一个易于应对变化的软件设计应该遵从高内聚,低耦合原则。
所谓内聚性,关注的是一个软件单位内部的关联紧密程度。因而高内聚追求的是关联紧密的事物应该被放在一起,并且只有关联紧密的事物才应该被放在一起。简单说,就是Unix的设计哲学:Do One Thing, Do It Well。
而耦合性,则是强调两个或多个软件单位之间的关联紧密程度。因而低耦合追求的是,软件单位之间尽可能不要相互影响。
这样的解释,对于很多人而言,依然会感到过于抽象。但如果我们进一步思考,就会意识到:看似神秘的内聚与耦合,正好对应最初的两个问题:
如何分? 当我们划分模块时,要让每个模块都尽可能高内聚;
如何合? 而当我们定义模块之间的API时,需要让双方尽可能低耦合。
首先将一个低内聚的模块首先拆分为多个高内聚的模块;然后再考虑这多个模块之间的API设计,以降低这些高内聚的软件单元之间的耦合度。
除了内聚与耦合之外,上面这幅图还揭示了另外一种关系:正交。具备正交关系的两个模块,可以做到一方的变化不会影响另外一方的变化。换句话说,双方各自独自变化,互不影响。
网友评论