设计模式
不看设计模式,写出来的代码永远不会漂亮
单例模式
保证一个类仅有一个实例,并提供一个访问它的全局访问点。惰性单例模式是指在使用的时候才回进行创建,并且把创建对象和管理单例的职责分开。
- 管理单例
1
2
3
4
5// 该方法无任何业务逻辑
getSingle(fn) {
let instance;
return () => (instance || (result = fn.apply(this, arguments)));
} - 创建对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14// 创建单例对象的义务逻辑
createLoginLayer() {
const div = document.createElement('div');
div.innerHTML = '我是登陆浮窗';
div.style.display = 'none';
document.body.appendChild(div);
return div;
}
const createSingleLoginLayer = getSingle(createLoginLayer);
// 点击的时候才创建
document.getElementById('loginBtn').onclick = function() {
const loginLayer = createSingleLoginLayer();
loginLayer.style.display = 'block';
}策略模式
定义一系列的算法,把他们一个个封装起来,并且使它们可以相互替换。一个策略模式程序至少有两部分组成,第一部分是一组策略类,封装了所有的算法。另一部分是环境类Context,接受请求然后委托到某个策略类。当一个类有过多的if else时,就需要考虑使用策略模式进行重构了。 - 利用组合、委托和多态思想,有效的避免多重条件选择语句
- 开发-封闭原则的完美支持,讲算法封装在strategy中,使得它容易切换、易于理解和扩展
- 复用
- 例子:表单校验
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45// 一系列的算法策略
const strategies = {
isNotEmpty(value, errorMsg) {
if (value === '') {
return errorMsg;
}
},
minLength(value, length, errorMsg) {
if (value.length < length) {
return errorMsg;
}
},
isMobile(value, errorMsg) {
if (!/(^1[3|5|8][0-9]{9}$)/.test(value)) {
return errorMsg;
}
}
}
// 接收请求,然后委托给策略算法
function Validator() {
this.cache = []; // 保存一系列的校验规则
}
Validator.prototype.add = function(value, rule, errorMsg) {
const ary = rule.split(':'); // 把strategy和参数分开,如minLength:6
this.cache.push(() => { // 把校验的步骤存起来
const strategy = ary.shift(); // 用户的strategy
ary.unshift(value); // 把input的value添加到参数列表
ary.push(errorMsg)
return strategies[strategy].apply(value, ary);
});
}
Validator.prototype.start = function() {
for (let i = 0; i < this.cache.length; i++) {
const func = this.cache[i];
const msg = func();
if (msg) {
return msg;
}
}
}
// 使用模式
const validator = new Validator();
validator.add(name, 'isNotEmpty', '用户名不能为空');
validator.add(paddword, 'minLength:6', '密码长度不能少于6位');
const errorMsg = validator.start(); // 通过判断errorMsg就可以判断是否进行表单请求
代理模式
代理模式是为一个对象提供一个代用品,通过代用品来对这个对象进行访问。常用的有保护代理、虚拟代理和缓存代理。保护代理是指代理负责拒绝一些不适合的请求,只接受适合的请求;虚拟代理是指等到需要使用时才去创建使用对象。缓存代理是指在代理把请求的结果缓存起来,下次再请求查找如果有缓存就直接取缓存。
- 虚拟代理实现图片预加载
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26// 该类的责职是加载图片
const myImage = (() => {
const imgNode = document.createElement('img');
document.body.appendChild(imgNode);
return {
setSrc(src) {
// 图片加载完成后再更新目标图片
imgNode.src = src;
}
}
})();
// 代理类的责职是预加载
const proxyImage = (() => {
// 新建一个image下载图片
const img = new Image();
img.onload = function() {
myImage.setSrc(this.src); // 下载完成后给目标对象设置
}
return {
setSrc(src) {
myImage.setSrc('file:///loading.png'); // 下载的时候显示个预加载菊花
img.src = src // 开始下载图片
}
}
})();
proxyImage.setSrc('https://www.baidu.png'); - 缓存代理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32// 乘法
function mult() {
const a = 1;
for (let i = 0; i < arguments.length; i++) {
a = a * arguments[i];
}
return a;
}
// 加法
function plus() {
const a = 0;
for (let i = 0; i < arguments.length; i++) {
a += arguments[i];
}
return a;
}
// 责职是缓存,可以给加减乘除复用
function createProxyFactory(fn) {
const cache = {}; // 用户缓存结果
return function() {
const args = Array.prototype.join.call(arguments, ',');
if (args in cache) { // 判断有无缓存,有则返回缓存
return cache[args];
}
// 缓存结果
return cache[args] = fn.apply(this, arguments);
}
}
const proxyMult = createProxyFactory(mult);
const proxyPlus = createProxyFactory(plus);
proxyMult(1, 2, 3, 4); // 输出24
proxyMult(1, 2, 3, 4); // 输出缓存的24
发布-订阅模式
发布-订阅模式又叫观察者模式,它定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知。优点是可以在时间和对象之间解耦,缺点是不容易追溯。
1 | const Event = (function() { |
命令模式
命令模式的作用主要是将动作的请求者
和动作的执行者
解耦。抽象一个command类,它定义了一个执行操作的接口(execute),还包含一个接收者(真正的执行命令的对象)。
1 | // 安装命令 |
组合模式
组合模式又叫部分整体模式,依据树形结构来组合对象,用来表示部分以及整体层次,利用对象多态性统一对待组合和单个对象。
1 | // 叶对象 |
模版方法模式
模版方法模式是一种利用继承方式,把一系列执行步骤抽象封装在父类(模板),然后在子类实现不同的算法(按照模板具体实现)。在JavaScript中也可以使用高阶函数实现,不需要使用继承。
1 | class Beverage { // 抽象类 |
享元模式
享元模式是一种用于性能优化的模式,核心是使用共享技术
来有效支持大量细粒度的对象。如果系统中因为创建了大量类似的对象而导致内存占用过高,享元模式就非常有用了。
责职链模式
责职链模式是使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系,将这些对象连成一条链,并沿着这条链传递该请求,知道有一个对象处理它为止。缺点是如果责职链过长时会影响性能。
1 | class Chain { |
中介者模式
用一个中介对象来封装一系列的对象交互,中介者使各个对象不需要县式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。当场景是对象与对象之间存在大量的关联,就可以考虑中介者模式。
若你觉得我的文章对你有帮助,欢迎点击上方按钮对我打赏
扫描二维码,分享此文章