1、what
软件实体(类、模块、函数)等应该对扩展是开放的,对修改是封闭的。
对扩展开放:有新的需求或变化时,可以对现有代码进行扩展,以适应新的情况。
对修改封闭:类一旦设计完成,可以独立完成其工作,而不要对类进行修改。
2、why
项目需求变迁过程中,经常会找到相关代码然后改写。扩展一个模块最常用的方式当然是修改它的源码,然而改动代码是一种危险的行为,刚刚改好一个bug很有可能不知不觉中引发了其他的bug。
那么既可以使用开放封闭原则的思想:当需要改变一个程序的功能或者给这个程序增加新的功能的时候,可以使用增加代码的方式,但是不允许改动程序的源代码。
3、how
封装变化,只将经常变化的部分进行抽象
举例说明:
①利用对象的多态性消除条件分支
var makeSound = function(animal) {
if (animal instanceof Duck) {
console.log('gagaga');
} else if (animal instanceof Chicken) {
console.log('gegege');
}
}
var Duck = function () {}
var Chicken = function () {}
makeSound(new Duck());
makeSound(new Chicken());
//多了一只狗,函数变成
var makeSound = function(animal) {
if (animal instanceof Duck) {
console.log('gagaga');
} else if (animal instanceof Chicken) {
console.log('gegege');
} else if (animal instanceof Dog) {
console.log('wangwang');
}
}
var Dog = function () {}
makeSound(new Dog());
利用多态把不变的部分隔离出来,把可变的部分封装起来
var makeSound = function (animal) {
animal.sound();
}
var Duck = function () {}
Duck.prototype.sound = function() {
console.log('gagaga');
}
var Chicken = function () {}
Chicken.prototype.sound = function() {
console.log('gegege');
}
makeSound(new Duck());
makeSound(new Chicken());
//多加动物狗却不用改变原有的makesound函数
var Dog = function () {}
Dog.prototype.sound = function () {
console.log('wangwang');
}
makeSound(new Dog());
②放置挂钩(hook)
是分离变化的一种方式。在程序有可能发生变化的地方放置一个挂钩,挂钩的返回结果决定了程序下一步的走向。
③使用回调函数
回调函数是一种特殊的挂钩。可以把易于变化的逻辑封装在回调函数里,然后把回调函数当做参数传入一个稳定的和封闭的函数中。
4、设计模式中的开放-封闭原则
好的设计都是经得起开放封闭-原则的考验。开放-封闭原则是编写一个好程序的目标,其他设计原则都是达到这个目标的过程。
①发布-订阅模式
待续
②模板方法模式
待续
③策略模式
待续
④代理模式
待续
⑤职责链模式
待续
5、开放-封闭原则的相对性
总会存在一些无法封闭的变化。。。
挑选出最容易发生变化的地方,然后构造抽象来封闭这些变化。
在不可避免发生修改的时候,尽量修改那些相对容易修改的地方。拿一个开源库来说,修改它提供的配置文件,总比修改它的源代码来的简单。
6、接受第一次愚弄
程序一开始就尽量遵循开放-封闭原则并不是一件容易的事。一方面很难未卜先知程序的变化在哪里,另一方面需求排期时间有限,可以说服自己去接受不合理代码的第一次愚弄。最初编写代码时候,假设变化永远不会发生,有利于迅速完成需求。当变化发生并且对接下来的工作造成影响的时候,再回过来封装这些变化的地方。确保不掉进同一个坑里。
参考:
《JavaScript设计模式与开发实践》 曾探著 AlloyTeam出品