开闭原则是设计原则基础的基础,其它原则均围绕开闭原则进行展开。开闭原则也就是一个软件实体应当对扩展开放,但对修改关闭。满足了开闭原则的设计,我们的系统将达到在设计稳定的基础上,方便的对软件进行扩展,插入新的功能模块的目的。
怎么样做的开闭原则呢?抽象化是关键,也是我们经常听到的“面象接口编程”,具体一点就是声明的变量的类型、函数的参数类型、函数的返回类型等要尽量使用抽象类和接口。开闭原则实际也是“对可变性的封装原则”,那就是“考虑你的设计中什么可能发生变化,找到一个系统的可变因素,将它封装起来”。把变化封闭起来也就是我们经常用到的继承,编写一个抽象类用子类来继承他,或者编写一个接口让子类去实现他。继承固然很好,既封装了变化又实现了复用,但却不能滥用,也就是说应当把继承仅仅当做封装变化的方法,而不应当被认为是从一般的对象生成特殊的对象。就如我们为了复用一个类里面的函数,便用另一个毫不相干的类来继承此类,这样的继承真可谓不伦不类。更糟糕一些那便是为复用而复用,编写一个抽象类去继承一个毫不相干的另一个抽象类(这样做可能是想复用此类中封装的变化),这样便把多于一种的可变性混合在了一起,混合的结果便是最终让我们思维混乱。
开头我们说开闭原则是设计原则基础的基础,因为其它的设计原则可以作为实现开闭原则的手段和工具。
1、 里氏代换原则:任何基类可以出现的地方,子类一定可以出现。
我们知道,实现开闭原则的关键是抽象化,而里氏代换原则中的基类和子类的继承关系正是抽象化的具体体现,所以里氏代换原则是对实现抽象化的具体步骤的规范。违反里氏代换原则一个最经典的例子便是把正方形设计成长方形的子类。
2、 依赖倒转原则:要依赖于抽象,不要依赖于实现。说的白一点就是要依赖于抽象类和接口不要依赖具体类,具体类也就是我们可以用new关键字实例化的类。依赖倒转原则是实现开闭原则的一个手段。
3、 合成/聚合复用原则:要尽量使用合成/聚合,而不是继承关系达到复用的目的。就如我们前面说的,如果为了复用,便使用继承的方式将两个不相干的类联系在一起,这样的方式是违反合成/聚合复用原则的,更进一步的后果那便是违反里氏代换原则。合成/聚合复用和里氏代换原则相辅相成,合成/聚合复用原则要求我们在复用时首先考虑合成/聚合关系,而里氏代换原则是要求我们在使用继承时,必须满足一定的条件。
4、 接口隔离原则:应当为客户端提供尽可能小的单独接口,而不要提供大的总接口。
5、 迪米特法则:一个软件实体应当尽可能少的与其他实体发生相互作用。
一个和其它实体关系很少的实体,在系统扩展时受的影响就会很小。当然接口隔离原则也是同样的道理,小的单独的接口,在系统扩展时受到的影响也会很小,所不同的是,迪米特法则(广义迪米特)限制的是相互作用的深度和广度,而接口隔离原则限制的只是相互作用的广度
在网上看到“板桥里人”的一篇文章http://www.jdon.com/artichect/ifelse.htm,你还在用if else吗?他在最后总结里说:“将if else用在小地方还可以,如简单的数值判断。但是如果按照你的传统习惯思维,在实现业务功能时也使用if else,那么说明你的思维可能需要重塑,你的编程经验越丰富,传统过程思维模式就容易根深蒂固,想靠自己改变很困难;建议接受专业头脑风暴训练。”此文通篇是如何用设计模式来替换if else语句的说词,不知道他这种极端的为了设计模式而设计模式的做法,是不是被他所谓的“头脑风暴”给刮疯癫所导致的。我也说一下我极端的想法,如果一个极端到没有任何可扩展性要求的系统,用面向对象的语言开发面向过程的程序,并无不可,面向对象里面的类只不过是我们分类存放函数的一个容器,其他别无用处,如果这时你还要写一大堆的接口和抽象类来显摆你所了解的设计模式,这简直就是强奸了设计模式,因为设计模式是为方便的扩展系统而生的,并不是让你拿来显摆的。关于各种对“将条件转移语句改写成为多态性”的代码重构做法的看法,我更倾向阎宏的的看法。
上而讲的“将条件转移语句改写成为多态性”的代码重构的做法有两大缺点。
第一大缺点,任何语言都提供条件转移功能,如果抛弃语言本身的一些功能,那么语言存在的价值又能体现在哪里呢?难道一个面向对象的语言,面向对象的特性就是这个语言的全部吗?条件转移本身并不是错误的,也不是什么罪恶,他更不是某些人眼里面向过程语言向面向对象语言过渡时一个权宜的保留。如果需要,设计师完全可以选择使用条件转移if else,并且不需要去接受什么头脑风暴的摧残。
第二大缺点,使用多态性代替条件转移意味着大量的类被创建出来。比如一个类如果有三个方法,每个方法都有一个三段的条件转移语句,如果将它们都用多态性代替的话,就会造出九个不同的类。很难想象设计师怎么能明白这九种组合成员之间的关系。
那么何时需要用多态性取代条件转移语句呢?
应当从“开闭”原则出发来做判断。如果一个条件转移语句确实封装了某种业务逻辑的可变性,那么将此种可变性封装起来就符合“开闭”原则的设计思想。但是,如果一个条件转移语句没有涉及重要的业务逻辑,或者不会随着时间的变化而变化,也不意味着任何的可扩展性,那么它就没有涉及任何有意义的可变性。这时候将这个条件转移语句改写成为多态性就是一种没有意义的浪费。阎宏将这种对多态性的滥用叫做“多态性污染”。如果再滥用一些设计模式,估计就应该叫“设计模式污染”了。