(本来想直接入主题找几个模式大写特写一番的,后来看了看,还是觉得先从整体出发比较好。)
设计模式是优秀的前辈软件工程师、架构师们智慧的精华集结,是编程文化的精髓组成部分。本菜在不断学习和思考了二十几个设计模式后,觉得还是先介绍一下整体的几个原则为妙。因为这些原则是设计模式的思想根基。
1、单一职责。
假设你有一台联想G480,它有上网、玩LOL、逛淘宝、写文章、学习等各项功能,你很满意。
假设你有一个类,它有上网、玩LOL、逛淘宝、写文章、看视频等各项功能,你就不一定满意了。为什么?因为客户要求你开发一款电视软件!!!
所以,在编程的世界里,虽然和生活有着密不可分的联系,但是二者还是有不少区别的。
单一职责原则,字面理解就是尽量只有一个职责。准确解释是,就一个类而言,应该仅有一个引起它变化的原因。如果一个类承担的职责过多,就等于把这些职责耦合在一起,将导致脆弱的设计。当发生变化时,设计会遭受意想不到的破坏!
如果我们把上述功能全部分开,那么我们无论是开发TV软件还是pad软件,只需要复用必要的单一功能类就OK了。
因此,在类设计中,如果你能够想到多于一个的动机去改变一个类,那么这个类就具有多于一个的职责,就要进行重新设计。
2、开放封闭+依赖倒转+里氏代换
为什么把这三个放在一起,请往下看。
(1)开闭原则是指软件实体(类、模块、函数等)应该开放扩展,关闭修改。我们在设计一个类的时候,应当尽量使它足够好,设计好了就尽量不修改了;有了新的需求,直接添加新类就解决了。
但是!无论类设计的多么封闭,还是会存在一些难以预料的变化。所以设计人员必须对于他设计的类应该对哪种变化封闭做出选择。先猜出最有可能发生变化的类,然后构造抽象来隔离变化。
一旦变化发生,应立即采取行动,即创建抽象类来隔离以后发生的同类变化。
开闭原则是OOD的核心,有可维护、可扩展、可复用、隔离性好等优点。我们在未来的开发设计中,应对程序中频繁变化的部分做出抽象,但不可对每个部分都刻意抽象,因为拒绝不成熟的抽象和抽象本身一样重要。
(2)农民老王是个“宝马迷”,一辈子就想买个宝马。终于有一天,老王走运买了张彩票,中了一千万,全部花光买了一款世界上独一无二的“宝马WD”,实现了宝马梦。但是!很不幸的是,还没开到家,轮胎废了。。。(本故事纯属虚构,如有雷同……)
你问老马该怎么办?凉拌呗!要不就不换,闲着,要不就换了全部的轮子,因为所有的都是独一无二的配件!老王表示没钱了~
如果老王买的是相对常见的X5/X6,换一个轮子倒也能接受。所以,由于老王没有学过依赖倒转,吃了大亏。
依赖倒转原则是说“抽象不应该依赖细节,细节要依赖抽象”,就是要针对接口编程,而不是实现。
用“倒转”一词也是有理由的。比如公司某工程师设计了一款宝马图纸,以后造宝马就按照它来,轮子统一是X型号。然而有一天,一大客户只想要Y型号轮子的宝马,公司表示很无奈,损失严重。如果当时没有规定必须用X型号轮子,只说四个轮子即可(规定了接口),就不会出现今天的问题。
为什么依赖了抽象的接口或抽象类就不怕更改了呢?这就需要里氏代换原则来解释了。
(3)里氏代换:子类型必须能够替换掉他们的父类型。即,一个软件实体如果使用的是一个父类的话,那么一定适用于其子类,而且它觉察不出来父类对象和子类对象的区别。
正是因为有了里氏代换,才使得开放封闭成为可能。因为子类能完全替换掉父类,这样就在父类关闭的条件下实现了子类扩展。
同样,依赖倒转中的子类依赖父类接口,高层模块和低层模块都依赖抽象才合理,也就很容易解释了。
3、迪米特。
迪米特法则:如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用,如果其中的一个类需要调用另一个类的某一个方法的话,可以通过第三方转发这个调用。
这个原则在以后的设计模式中体现的非常普遍,比如单例、代理、外观等等,以后再详述。
迪米特法则的根本就是要求松耦合。在类的结构设计上,每个类都应当尽量降低成员的访问权限。类之间的耦合越弱,越有利于复用,对类的更改引起的程序变化也越小。
4、合成/聚合复用。
在UML的类图中,我们说过聚合、组合之间的联系与区别。聚合是一种弱关系,体现的是A包含B,B不是A的一部分;组合是一种强关系,体现的是A与B之间的整体-部分关系。
合成/聚合复用原则的好处是,优先使用对象的合成/聚合将有助于你保持每个类被封装,并被集中在单个任务上。这样类和类继承层次会保持较小规模,并且不太可能增长为不可控制的庞然大物。
它要求我们尽量少用继承(只有在类之间符合 is - a时可以使用),防止类的盲目扩大。在“桥接模式”中体现比较深刻。
这其实也体现了单一职责的思想。尽量减少类的功能,降低耦合。
总结:个人认为,设计模式中的思想是系统化的、各自联系的。各个思想之间都有联系,需要我们在设计过程中仔细思考,分析实际需求,把它们对应到各自适合的具体设计模式中。