设计模式的基本原则
1.背景
设计模式是一套被反复使用、多数人知晓的,经过分类的、代码设计经验的总结
其目的是为了代码可重用性、让代码更容易被他人理解、保证代码可靠性
是解决方案,重心是思想,目的是为了让代码更简便,简洁,可靠,易理解,易复用,也就是常说的高内聚,低耦合
2.基本原则有哪些?
javascript中关于设计模式的基本原则,常见的有如下:
- 单一职责原则
- 最少知识原则
- 开放封闭原则
- 依赖倒置原则
单一职责原则
一个类只负责一个功能领域中的相应职责,在javascript中,使用到类的场景并不多,单一职责原则更多地运用在对象或者方法上
如果一个方法承担过多的职责,那么在需求的变迁过程中,需要改写这个方法的可能性就会越大
单一职责原则是最简单也是最难正确运用的原则,要明确的是,并不是所有的职责都应该一一分离:可参考 设计模式之单例模式
- 如果随着需求的变化,有两个职责总是同时变化,那就不必分离他们
比如在 ajax 请求的时候,创建 xhr 对象和发送 xhr 请求几乎总是在一起的,
那么创建 xhr 对象的职责和发送 xhr 请求的职责就没有必要分开
最少知识原则
对象(函数、模块等)应当尽可能少地与其他实体发生相互作用
在设计程序时,应当尽量减少对象之间的交互。如果两个对象之间不直接通信,那么这两个对象就不要发生直接的相互联系
常见的做法是引入一个第三者对象(中介者),来承担这些对象之间的通信作用。如果一些对象需要向另一些对象发起请求,可以通过第三者对象来转发这些请求。
javaScript设计模式中的使用最少知识原则的有:
- 中介者模式:通过增加一个中介者对象,让所有的相关对象都通 过中介者对象来通信,而不是互相引用。所以,当一个对象发生改变时,只需要通知中介者对象即可
- 外观模式:对用户屏蔽一组子系统的复杂性。为用户提供一个简单易用的接口,高层接口会把用户的请求转发给子系统来完成具体的功能实现
开放封闭原则
在面向对象的程序设计中,开放封闭原则是最重要的一条原则
其思想是当需要改变一个程序的功能或者给这个程序增加新功能的时候,可以使用增加代码的方式,但是不允许改动程序的源代码
- 开放指的是面向扩展开放:模块的行为是能够被扩展的。当应用程序的需求变化时,我们可以使模块表现出全新的或与以往不同的行为,以满足新的需求
- 封闭指的是面向修改封闭:模块的源代码不能被侵犯,任何人都不允许修改已有的源代码
//检测字符串
//checkType('165226226326','mobile')
let checkType = function(str, type) {
switch (type) {
case 'email':
return /^[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+$/.test(str);
case 'mobile':
return /^1[3|4|5|7|8][0-9]{9}$/.test(str);
case 'tel':
return /^(0\d{2,3}-\d{7,8})(-\d{1,4})?$/.test(str);
default:
return true;
}
}
该方法用起来是可以的,但是如果想要添加功能,就需要往函数内部添加case,这样也会导致整个 API 变得臃肿,难维护,违反封闭开放原则
可以改造成如下:
let checkType = (function() {
let rules = {
email(str) {
return /^[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+$/.test(str);
},
mobile(str) {
return /^1[3|4|5|7|8][0-9]{9}$/.test(str);
},
tel(str) {
return /^(0\d{2,3}-\d{7,8})(-\d{1,4})?$/.test(str);
};
//暴露接口
return {
//校验
check(str, type) {
return rules[type] ? rules[type](str) : false;
},
//添加规则
addRule(type, fn) {
rules[type] = fn;
}
}
})();
依赖倒置原则
高层次的模块不应该依赖于低层次的模块,他们都应该依赖于抽象,抽象不应该依赖于具体实现,具体实现应该依赖于抽象
例如人吃东西, 可以吃米饭、馒头、面条等等,这里人就是高层的模块,人要活着就需要依赖吃的行为
吃的行为是一个抽象的行为,可以米饭、馒头等等
而不能变成人要依赖吃米饭或者吃馒头
这样则降低了客户与实现模块间的耦合
// 吃的抽象类
class eat {
go(name) {
return `吃${name}`
}
}
//实现类(细节)
class Rice extends eat {
go() {
return "吃米饭"
}
}
class Noodle extends eat {
go() {
return "面条"
}
}
//人类
class people {
constructor(name) {
this.name = name
}
gotoEat(food) {
console.log(this.name, food.go())
}
}
const rice = new Rice()
const noodle = new Noodle()
const peopleA = new people("小明")
peopleA.gotoEat(rice)
总结
让程序一开始就尽量遵守开放-封闭原则,并不是一件很容易的事情,需要预测容易变化的部分
挑选出最容易发生变化的地方,然后构造抽象来封闭这些变化
在不可避免发生修改的时候,尽量修改那些相对容易修改的地方。例如用修改配置文件,代替修改它的源代码