设计模式概略 + 常用设计模式
包括单例模式、策略模式、代理模式、发布订阅模式、命令模式、组合模式、装饰器模式、适配器模式
# 设计模式概略
# 什么是设计模式
定义:在软件设计过程中,针对特定问题的简洁而优雅的解决方案
# SOLID 五大设计原则
- Single:单一职责原则
- 一个程序只做好一件事
- Open:开放封闭原则
- 对扩展开放,对修改封闭
- Liskov:里氏置换原则
- 子类能覆盖父类,并能出现在父类出现的地方
- Interface:接口独立原则
- 保持接口的单一独立
- Dependency:依赖导致原则
- 使用方法只关注接口而不关注具体类的实现
# 为什么需要设计模式
- 易读性
- 使用设计模式能够提升代码的可读性,提升后续开发效率
- 可扩展性
- 使用设计模式对代码解耦,能很好地增强代码的易修改性和扩展性
- 复用性
- 使用设计模式可以重用已有的解决方案,无需再重复相同工作
- 可靠性
- 使用设计模式能够增加系统的健壮性,使代码编写真正工程化
# 常见设计模式
# 单例模式
定义:唯一 & 全局访问。保证一个类仅有一个实例,并提供一个访问它的全局访问点。
应用场景: 能被缓存的内容,例如登陆弹窗。
# 策略模式
定义:定义一系列的算法,把他们一个个封装起来,并且使他们可以相互替换。把看似毫无联系的代码提取封装、复用,使之更容易被理解和扩展。
应用场景:要完成一件事情,有不同的策略。例如绩效计算、表单验证规则。
示例:
// 策略 | |
const calculateBonus = (level, salary) => { | |
switch (level) { | |
case 's ': { | |
return salary * 4; | |
} | |
case 'a': { | |
return salary * 3; | |
} | |
case 'b': { | |
return salary * 2; | |
} | |
default: { return 0; } | |
} | |
return strategies[level](salary); | |
}; | |
calculateBonus("s", 20000); // 80000 | |
calculateBonus("a", 10000); // 30000 |
// 策略 | |
const strategies = { | |
s: (salary) => { | |
return salary * 4; | |
}, | |
a: (salary) => { | |
return salary * 3; | |
}, | |
b: (salary) => { | |
return salary * 2; | |
}, | |
}; | |
const calculateBonus = (level, salary) => { | |
return strategies[level](salary); | |
}; | |
calculateBonus("s", 20000); // 80000 | |
calculateBonus("a", 10000); // 30000 |
# 代理模式
定义:为一个对象提供一个代用品或占位符,以便控制对它的访问。替身对象可对请求预先进行处理,再决定是否转交给本体对象。
应用场景:当我们不方便直接访问某个对象时,或不满足需求时,可考虑使用一个替身对象来控制该对象的访问。
示例:
// 原生函数 | |
const rawImage = (() => { | |
const imgNode = document.createElement("img"); | |
document.body.appendChild(imgNode); | |
return { | |
setSrc: (src) => { | |
imgNode.src = "./loading.gif"; | |
const img = new Image(); | |
img.src = src; | |
img.onload = () => { | |
imgNode.src = this.src; | |
} | |
}, | |
}; | |
})(); | |
rawImage.setSrc("http://xxx.gif"); |
// 原生函数 | |
const rawImage = (() => { | |
const imgNode = document.createElement("img"); | |
document.body.appendChild(imgNode); | |
return { | |
setSrc: (src) => { | |
imgNode.src = src; | |
}, | |
}; | |
})(); | |
// 代理函数 | |
const proxyImage = (() => { | |
const img = new Image(); | |
img.onload = () => { | |
rawImage.setSrc(this.src); | |
}; | |
return { | |
setSrc: (src) => { | |
rawImage.setSrc("./loading.gif"); | |
img.src = src; | |
}, | |
}; | |
})(); | |
proxyImage.setSrc("http: //xxx.gif"); |
# 发布订阅模式
定义:对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知。
应用场景:DOM 事件、消息通知
示例:
class PubSub { | |
constructor() { | |
this.subscribers = {}; | |
} | |
// 订阅 | |
subscribe(type, fn) { | |
let listeners = this.subscribers[type] || []; | |
listeners.push(fn); | |
} | |
// 取消订阅 | |
unsubscribe(type, fn) { | |
let listeners = this.subscribers[type]; | |
if (!listeners || !listeners.length) return; | |
this.subscribers[type] = listeners.filter((v) => v !== fn); | |
} | |
// 触发订阅事件 | |
publish(type, ...args) { | |
let listeners = this.subscribers[type]; | |
if (!listeners || !listeners.length) return; | |
listeners.forEach((fn) => fn(...args)); | |
} | |
} | |
let ob = new PubSub(); | |
ob.subscribe("add", (val) => console.log(val)); | |
ob.publish("add", 1); |
# 命令模式
定义:执行某些特定事情的指令
应用场景:富文本编辑器工具栏
示例:
// 设置命令 | |
const setCommand = (button, command) => { | |
button.onclick = () => { | |
command.execute(); | |
}; | |
}; | |
// 业务逻辑 | |
const MenuBar = { | |
refresh: () => { | |
console.log("refresh"); | |
}, | |
}; | |
const RefreshCommand = (receiver) => { | |
return { | |
execute: () => { | |
receiver.refresh(); | |
}, | |
}; | |
}; | |
const refreshMenuBarCommand = RefreshCommand(MenuBar); | |
// 绑定按钮和命令 | |
setCommand(refreshButton, refreshMenuBarCommand); |
# 组合模式
定义:用小的子对象来构建更大的对象,将对象组合成树形结构,以表示 “部分 - 整体” 的层次结构。
应用场景:从 is-a 到 has-a
示例:
class MacroCommand { | |
constructor() { | |
this.commands = []; | |
} | |
// 添加子对象逻辑 | |
add(command) { | |
console.log(this); | |
this.commands.push(command); | |
} | |
// 执行父对象逻辑 | |
execute() { | |
for (let i = 0; i < this.commands.length; i++) { | |
this.commands[i].execute(); | |
} | |
} | |
} | |
const macroCommand = MacroCommand(); | |
const openCommand = { | |
execute: () => { | |
console.log('open '); | |
} | |
} | |
const closeCommand = { | |
execute: () => { | |
console.log('close'); | |
} | |
} | |
macroCommand.add(openCommand); | |
macroCommand.add(closeCommand); | |
macroCommand.execute(); |
# 装饰器模式
定义:能够在不改变对象自身的基础上,在程序运行期间给对象动态地添加职责。
应用场景:数据上报、统计函数执行时间
示例:
const ajax = (type, url, param) => { | |
console.log(param); | |
} | |
const getToken = () => { | |
return 'Token'; | |
} | |
ajax(); | |
getToken(); |
// 添加职责 | |
Function.prototype.before = function (beforeFn) { | |
return (...rest) => { | |
beforeFn.apply(this, rest); | |
return this(...rest); | |
}; | |
}; | |
let ajax = (type, url, param) => { | |
console.log(param) | |
} | |
const getToken = () => { | |
return 'Token' | |
} | |
ajax = ajax.before((type, url, param) => { | |
param.token = getToken() | |
}) |
# 适配器模式
定义:解决两个软件实体间的接口不兼容问题,改变已有的接口,就能够使它们协同作用。
应用场景:接口不兼容的情况
示例:
const aMap = { | |
show: () => { | |
console.log('开始渲染地图A'); | |
} | |
}; | |
const bMap = { | |
display: () => { | |
console.log('开始渲染地图B'); | |
} | |
}; | |
const renderMap = (type) => { | |
if (type === 'a') { | |
aMap.show(); | |
} else if (type === 'b') { | |
bMap.display(); | |
} | |
}; |
const aMap = { | |
show: () => { | |
console.log('开始渲染地图A'); | |
} | |
}; | |
const bMap = { | |
display: () => { | |
console.log('开始渲染地图B'); | |
} | |
}; | |
// 适配层 | |
const bMapAdapter = { | |
show: () => { | |
return bMap.display(); | |
} | |
}; | |
const renderMap = (map) => { | |
if (map.show instanceof Function) { | |
map.show(); | |
} | |
}; | |
renderMap(aMap); | |
renderMap(bAdapter); |
参考:
字节青训营课程