第二十章 开放-封闭原则
20.2 开放和封闭
开放-封闭原则的思想:当需要改变一个程序的功能或者给这个程序增加新功能的时候,可以使用增加代码的方式,但是不允许改动程序的源代码。
20.3 用对象的多态性消除条件分支
利用多态的思想,把程序中不变的部分隔离出来,然后把可变的部分封装起来。
1 | var makeSound = function (animal) { |
20.4 找出变化的地方
通过封装变化的方式,可以把系统中稳定不变的部分和容易变化的部分隔离开来。在系统的演变过程中,只需要替换容易变化的部分,如果这些部分已经被封装好,替换起来也相对容易。
除了利用对象的多态性之外,还有其他方式可以帮助编写遵守开放-封闭原则的代码。
20.4.1 放置挂钩
在程序有可能发生变化的地方放置一个挂钩,挂钩的返回结果决定了程序的下一步走向。
关于模板方法模式中的挂钩应用,可以参考第十一章。
20.4.2 使用回调函数
回调函数是一种特殊的挂钩。可以把一部分易于变化的逻辑封装在回调函数里,然后把回调函数当作参数传入一个稳定和封闭的函数中。当回调函数被执行的时候,程序就可以因为回调函数的内部逻辑不同,而产生不同的结果。
20.5 设计模式中的开放-封闭模式
20.5.1 发布-订阅模式
发布-订阅模式用来降低多个对象之间的依赖关系,可以取代对象之间硬编码的通知机制,一个对象不用再显式地调用另一个对象的某个接口。当有新的订阅者出现时,发布者的代码不需要进行修改;当发布者需要改变时,订阅者也不会受到影响。
20.5.2 模板方法模式
在一个运用了模板方法模式的程序中,子类的方法种类和执行顺序都是不变的,所以把这部分逻辑抽出来放到父类的模板方法里面;而子类的方法具体怎么实现是可变的,于是把这部分变化的逻辑封装到子类中。增加新的子类能给系统增加新的功能,不需要改动抽象父类以及其他的子类。
20.5.3 策略模式
策略模式将各种算法都封装成单独的策略类,可以被交换使用。策略和使用策略的客户代码可以分别独立进行修改而互不影响。增加新的策略类不用修改之前的代码。
20.5.4 代理模式
预加载图片的功能和给图片设置 src
的功能被隔离在两个函数里,它们可以单独改变而不影响。myImage
不知晓代理的存在,它可以继续专注于自己的职责——给图片设置 src
。
20.5.5 职责链模式
第十四章中,把一个巨大的订单函数拆成 3 个函数。这 3 个函数通过职责链连接在一起,客户的请求会在这条链条里依次传递。
当增加一个新类型的订单函数时,不需要改动原有的订单函数代码,只需在链条中增加一个新的节点。
20.6 开放-封闭原则的相对性
实际上,让程序保持完全封闭是不容易做到的。就算技术上做得到,也需要花费太多的时间和精力。而且让程序符合开放-封闭原则的代价是引入更多的抽象层次,更多的抽象有可能会增大代码的复杂度。
- 挑选出最容易发生变化的地方,然后构造抽象来封闭这些变化。
- 在不可避免发生修改的时候,尽量修改那些相对容易修改的地方。