每种设计模式都是为了让代码迎合其中一个或多个原则而出现的,
设计原则本身已经融入了设计模式之中。
常用设计原则
主要有以下几种
- 单一职责原则
- 最少知识原则
- 里氏替换原则
- 依赖倒置原则
- 接口隔离原则
- 合成复用原则
本文只讲:单一职责原则、最少知识原则、开放-封闭原则三种
一、单一职责原则(SRP)
就一个类而言,应该仅有一个引起它变化的原因
在 JavaScript 中,需要用到类的场景并不 太多,单一职责原则更多地是被运用在对象或者方法级别上,体现为:一个对象(方法)只做一件事情。
SRP原则在很多设计模式中都有着广泛的运用,例如:
- 代理模式
- 迭代器模式
- 单例模式和装
- 饰者模式。
使用原则
1. 何时应该分离职责
SRP 原则是所有原则中最简单也最难正确运用的原则之一。 要明确的是,并不是所有的职责都应该一一分离。
- 如果随着需求的变化,有两个职责总是同时变化,那就不必分离他们。比如在 ajax 请求的时候,创建 xhr 对象和发送 xhr 请求几乎总是在一起的,那么创建 xhr 对象的职责和发送 xhr 请求的职责就没有必要分开。
- 职责的变化轴线仅当它们确定会发生变化时才具有意义,即使两个职责已经被耦合在一起,但它们还没有发生改变的征兆,那么也许没有必要主动分离它们,在代码需要重构的时候再进行分离也不迟。
2. 违反SRP原则
在人的常规思维中,总是习惯性地把一组相关的行为放到一起,如何正确地分离职责不是一 件容易的事情。
一方面,我们受设计原则的指导,另一方面,我们未必要在任何时候都一成不变地遵守原则。
在方便性与稳定性之间要有一些取舍。具体是选择方便性还是稳定性,并没有标准答案,而 是要取决于具体的应用环境。
优缺点
-
优点: 降低了单个类或者对象的复杂度,按照职责把对象分解成更小的粒度, 这有助于代码的复用,也有利于进行单元测试。当一个职责需要变更的时候,不会影响到其他的职责。
-
缺点: 最明显的是会增加编写代码的复杂度。当我们按照职责把对象分解成更小的粒度之后,实际上也增大了这些对象之间相互联系的难度。
二、最少知识原则(LKP)
对象(函数、模块等)应当尽可能少地与其他实体发生相互作用。
思想
单一职责原则意图把对象划分成较小的粒度,以提高对象的可复用性。
在设计程序时,应当尽量减少对象之间的交互。如果两个对象之间不直接通信,那么这两个对象就不要发生直接的相互联系。常见的做法是引入一个第三者对象(中介者),来承担这些对象之间的通信作用。如果一些对象需要向另一些对象发起请求,可以通过第三者对象来转发这些请求。
应用
最少知识原则在设计模式中体现得最多的地方是中介者模式和外观模式
中介者模式: 中介者模式很好地体现了最少知识原则。通过增加一个中介者对象,让所有的相关对象都通 过中介者对象来通信,而不是互相引用。所以,当一个对象发生改变时,只需要通知中介者对象即可。
外观模式: 对用户屏蔽一组子系统的复杂性。为用户提供一个简单易用的接口,高层接口会把用户的请求转发给子系统来完成具体的功能实现。
封装: 封装在很大程度上表达的是数据的隐藏。一个模块或者对象可以将内部的数据或者实现细节隐藏起来,只暴露必要的接口 API 供外界访问。
三、开放-封闭原则(OCP)
对象(类、模块、函数等)应该是
可扩展但不可修改的。
在面向对象的程序设计中,开放-封闭原则(OCP)是最重要的一条原则。
思想
当需要改变一个程序的功能或者给这个程序增加新功能的时候,可以使用增加代码的方式,但是不允许改动程序的源代码。
应用
1. 利用多态消除过多的条件分支语句
过多的条件分支语句是造成程序违反开放-封闭原则的一个常见原因。
解决办法: 利用对象的多态性(同一个方法名的不同实现方式)
2. 找出变化的地方:放置挂钩、使用回调函数
出程序中将要发生变化的地方,通过封装变化的方式,可以把系统中稳定不变的部分和容易变化的部分隔离开来。
在系统演变过程中,我们只需要替换那些容易变化的部分,如果这些部分是已经被封装好的,那么替换 起来也相对容易。
- 放置挂钩(hook)
在程序有可能发生变化的地方放置一个挂钩,挂钩的返回结果决定了程序的下一步走向。
- 使用回调函数(callback) 回调函数是一种特殊的挂钩。我们可以把一部分易于变化的逻辑封装在回调函数里,然后把 回调函数当作参数传入一个稳定和封闭的函数中。当回调函数被执行的时候,程序就可以因为回 调函数的内部逻辑不同,而产生不同的结果。
OCP原则在设计模式中的应用
几乎所有的设计模式都是遵守开放-封闭原则的,OCP原则是编写一个好程序的目标,其他设计原则都是达到这个目标的过程。
- 发布-订阅模式: 当有新的订阅者出现时,发布者的代码不 需要进行任何修改;同样当发布者需要改变时,也不会影响到之前的订阅者。
- 模板方法模式: 通过封装变化来提高系统扩展性
- 策略模式
- 代理模式
- 职责链模式
小结
- 让程序一开始就尽量遵守开放-封闭原则,并不是一件很容易的事情,需要
预测容易变化的部分。 - 挑选出最容易发生变化的地方,然后构造抽象来封闭这些变化。
- 在不可避免发生修改的时候,尽量修改那些相对容易修改的地方。例如用修改配置文件,代替修改它的源代码。