软件架构的灵活设计
板桥里人html
软件架构如同人的骨架,不但要在总体上有骨感,并且细部须要不少骨关节链接,骨关节能够把两根大骨衔接在一块儿,两根大骨由此造成了松耦合,这样整个骨架的活动就灵活自如了。
软件架构也应该如此,组件之间实现松耦合,相似积木或乐高玩具同样,经过组件模块之间的松耦合构建成一个灵活自如的软件系统。算法
松耦合表明对象之间关系比较松散,甚至没有热河关系,松耦合能够带来软件架构的灵活性,意味着扩展性、可维护性获得提升,复杂性下降了。网络
目前,组件模块之间松耦合常见有两种方式:接口和消息,这二者如同骨关节同样,链接着松耦合的双方。消息比接口更加松耦合,由于接口方法有可能改变,致使接口的使用者跟着变化,而经过消息,消息生产者只要发送消息到消息系统就能够了,再也不管是谁来接受消费这个消息,消息生产者由此和消费者彻底解耦了。架构
复杂性
假设原来是只有两个组件进行交互:ide
后来需求不断扩展变化,增长了第三个组件,那么这三个组件之间任意交互,复杂度就会提升:
复杂度以指数级的增加是惊人的,当咱们增长到六个组件,复杂度将是惊人的:函数
天然界是如何应对这复杂呢?这在物理中被称为构造定律 Constructal Law, 仅有的咱们知晓的大天然是如何指导复杂演化的规律(能够说是上帝创造万物的方式),是由Adrian Bejan于1995创立的构造定律:学习
For a finite-size system to persist in time (to live), it must evolve in such a way that it provides easier access to the imposed currents that flow through it.flex
对于一个有限大小的持续活动的系统,它必须以这种方式发展演进:它提供了一种在自身元素之间能量(current)更容易流动的方式。
构造定律被提出做为对全部的设计和进化进行论述的物理学第一个原则。它认为形状和结构的出现是为了更好地流动。大天然的这种设计反映了这样趋势:它鼓励实体能更容易流动 – 在一样的每单位能量消耗的状况下能更容易移动。例如雨滴汇集在一块儿,汇成河流,江流入海成大海,大天然的设计使得水更容易流动。
构造定律致力于描述能量和物质在物理网络(如河流)和生物网络(如血管)中的流动,这个理论提出,若是一个流体系统(flow system)要继续存在(好比,生存),那么他必须始终提供更容易的方式来得到这个系统中的流体。换句话说,系统应该致力于将能量消耗减小到最低限度,而同时将消耗单位能量产生的熵提升到最大限度
Bejan相信,进化实质上是这么一个过程,即生物体不断的重组他们自身,以使能量和物质可以尽量迅速高效的经过他们。更好的流体结构(flow structure),无论它们是动物仍是河流,将取代那些较差的结构。。
对于咱们软件系统,如何设计出一种结构以促成流经这个结构的用户请求能更有效地得到响应呢?很显然,前面那种多个组件发生任意关联的方式确定是不经济的,熵值反作用很大,若是变成如下这种结构,组件可以实现分组,三个元素是一组,另一组是四个元素,组与组之间经过一个表明元素关联:
那么,如何进行这种有合有分的设计呢?
SOLID原则是关键:
S - Single Responsibility Principle 单一职责,简称SRP
O - Open/Closed Principle 开闭原则
L - Liskov Substitution Principle 里氏替换原则 简称LSP
I - Interface Segregation Principle接口分离 简称ISP原则
D - Dependency Inversion Principle 依赖反转原则 DIP
这五大设计原则中,单一职责功能是关键,它意味着它只作一件事。
它与让事情DRY原则是一致的:每个知识都必须有一个单一的、明确的、在一个系统内的权威明确表示。不要重复,须要干脆。 程序中的每个重要的功能都应该在源代码中的一个地方实现,将业务规则、长表达式,if语句、数学公式和元数据等各自放在一个地方。
单一职责也与委托原则有关,只有放弃一些才能得到一些,有舍有得,放弃的就是委托其余类实现:不要本身作全部的事情,能够委托给相应的类去完成。
由于每一个组件都秉持单一职责,组件之间才可能发生惟一的一个关联关系:url
松耦合与高凝聚
软件设计者应该是分离应该分离事物,而不是将本应一块儿考虑的事情进行分离。
-Kent Beckspa
实现松耦合,不是简单地切分就能够,须要如庖丁解牛遵循必定天然之道,若是切分了不应切分的事物反而事倍功半。因此,反者道之动,咱们首先了解什么是不应分离的事情,才明白剩余的才是应该分离的。
不可分离的关系也称为凝聚,而凝聚好像是一种耦合,是松耦合的反面,那么如何区分耦合与凝聚的区别呢?凝聚与耦合都是表达模块之间关系的名词,可是存在不一样含义。
凝聚有高凝聚和低凝聚之分,高凝聚是指那些自然聚合在一块儿的紧密关系,好比总体是由部分组成的关系,业务领域中业务焦点等等,高凝聚要么是表明业务强关系,要么是体现其余优良性状的关联,如可靠性、健壮性、可重用性和可理解性。
而低凝聚则是与不良特征有关,包括难以维持、难以重用和难以理解。在业务领域表现为那些无关紧要的关联关系,没法肯定的关联关系,多对多的关联等,当你发现两个实体之间是多对多关系时,可能意味着你是执着于关系角度没事找事地找出它们之间的关系,从另一个角度来看,万物互联,中医还认为天人合一,这些都不是科学的分析方法,科学分析方法是将关系切分为高凝聚与低凝聚两种,而后排除低凝聚,只留下高凝聚,其他都是松耦合,这样才有的放矢。
咱们还能够从类的方法功能上判断高凝聚,固然前提是你的实体必须有方法行为,而不是只有属性的失血模型。若是嵌入在类中的功能有不少共同之处,那么系统中的内聚性就会增长。也就是说,若是一个类的方法行为确实有不少共同之处,那么表明这些方法与类确实有一种内聚性,若是随着方法行为增多,这种类的内部高聚性就愈来愈强。
高内聚可以提升了模块的可重用性,模块中不少功能都有共同点,所以开发人员很容易在模块中找到他们想寻找的功能。
从另一个方面来看,若是业务领域的逻辑改变,却不多影响周围其余模块,这说明系统的可维护性提升了,由于你的业务逻辑都已经收敛到应该收敛的地方,不会散落各处,因此就不多影响其余模块。如同玉皇大帝为何要找回七仙女,由于他们都是仙人,仙人就该回(收敛或凝聚)到他们本身的地方。
同时,当代码有高凝聚时,意味着模块的复杂性下降。由于变得有条理,复杂性天然下降。
在实践中能够经过下面几个途径寻找高凝聚,首先是结构上的总体部分组成或组合关系,其次是计算的总步骤与子步骤之间的关系。固然还能够从方法行为方面来区分,因为涉及某个单一职责功能的各个环节的方法函数,好比x+y+z这个计算职责涉及到了两次加法函数,它们无疑是高凝聚在一块儿。
同理,对于一个输入输出过程,涉及到一系列任务,无疑这些任务功能都高凝聚在一块儿了。
了解了高凝聚的关系之后,其他剩余的就是耦合了,这个时候组件之间的强关系就用耦合这个贬义词表达。咱们的目的是保留下好的强关系也就是高凝聚,去除很差的强关系也就是低耦合。高凝聚,低耦合是咱们将低复杂性提升灵活性设计中重要的宗旨。
消息传递
咱们知道一个对象类是由字段属性和方法行为组成的,高凝聚低耦合的设计原则是要落实到这两个基础元素上。若是一个类(A)将另一个类(B)做为本身的字段属性,那么咱们就认为A和B的关系是一种组合性质的高凝聚关系,这种组合关系是一种总体由部分组成的强的结构性关系,也就是说,如同在数据表结构中确立外键关联结构同样,A和B之间的关系会伴随A的诞生直至消亡,全生命周期陪伴。
除了静态的结构关系之外,高凝聚低耦合还表达为对象的方法行为上,这须要经过分配职责来实现。什么是职责?它包括三个方面:
- 对象应该执行的动做;
- 对象包含的知识如:算法 约束 规格 描述;
- 当前对象影响其余对象的主要因素。
一个对象不能只有字段属性和有关属性的setter/getter,应该将业务职责分配给对象,使得对象有形有态。按照高凝聚(DDD聚合)原则分配。遵循假设:“若是没有这个职责,会怎样”。
以报童向买报人收钱这个案例为例:
报童应该向顾客收取两块钱的买报费,他是直接把顾客的钱包拿过来从中掏出两块钱,仍是请求顾客本身从钱包里掏出两块钱呢?无疑是后者,可是一般咱们是这么写代码:
public class 报童{
Wallet theWallet = myCustomer.getWallet(); //报童从顾客手里拿过钱包
if (theWallet.getTotalMoney() > 2.00) {
theWallet.subtractMoney(2.00);//报童直接从顾客钱包掏出两块钱
}
}
正常设计代码应该以下:
public class 报童{
int paidAmount = myCustomer.getPayment(2.00);
if (paidAmount == 2.00) {
// say thank you
}
}
二者差异就是第一个错误设计师直接从顾客那里拿出钱包myCustomer.getWallet();然后者只是告诉顾客你该掏出两块钱了myCustomer.getPayment(2.00)。
这二者的区别是什么?Tell, Don't Ask原则:
报童只要告诉(Tell)顾客作什么(付钱),而不直接参与怎么作(Ask,你的钱够吗?够才能买)。 报童只给顾客一个命令,而没必要关注顾客是如何执行这个命令。
Tell, Don't Ask原则可以让咱们保证两个组件之间在动做上不会发生过于细节过多的耦合,而只是经过一个消息就能完成,经过发送消息告诉对方我须要什么,而不是直接把对方拎过来搜身。至此,咱们已经完成了两个组件之间最大松耦合,实现了架构设计的最大可能的灵活性。
总结:本文探讨了软件架构复杂性必然如天然界任何事物同样不断发展,做为软件架构师如何学习大天然的巧妙设计,如同庖丁解牛同样切分复杂性为单一职责,再从结构和行为的高凝聚关系方面进行组合或消息传递,从而真正实现高凝聚低耦合的灵活性目标。
http://www.jdon.com/artichect/flexable.html