JS中的设计模式
是什么
在软件设计过程中,针对特定问题的简介而优雅的解决方案
经验总结=>合理运用=>解决问题
SOLID设计原则
S单一职责原则
一个模块只做一件事
O开放封闭原则
对扩展开放,对修改封闭
L里氏置换原则
子类能覆盖父类,并能出现在父类出现的地方
I接口独立原则
保持接口的单一独立
D依赖导致原则
使用方法只关注接口而不关注具体类的实现
为什么
易读性
使用设计模式能提升代码的可读性,提升后续开发效率
可扩展性
使用设计模式对代码解耦,能很好的增强代码的易修改性和扩展性
复用性
使用设计模式可以重用已有的解决方案,无需重复相同工作
可靠性
使用设计模式能增加系统的健壮性,使代码编写真正工程化
常见设计模式
单例模式
定义
唯一&全局访问。保证一个类仅有一个实例,并提供一个访问它的全局访问点
应用场景
能被缓存的内容?,如登录弹窗
Example
Before
const createLoginLayer = () => {
const div = document.createElement("div");
div.innerHTML = "登录浮窗";
div.style.display = "none";
document.body.appendChild(div);
return div;
};
document.getElementById("loginBtn").onclick = () => {
const loginLayer = createLoginLayer();
loginLayer.style.display = "block";
};
After
const getSingle = (fn) => {
let result;
return (...rest) => {
return result || (result = fn.apply(this, rest));
};
};
const 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 = () => {
const loginLayer = createSingleLoginLayer();
loginLayer.style.display = "block";
};
优点
节省内存,提高性能。(相当于缓存)
策略模式
定义
定义一系列的算法,把它们一个个封装起来,并使他们可以互相替换。把看似毫无联系的代码提取封装、复用,使之更容易拓展和理解。
应用场景
要完成一件事,有不同的策略。例如绩效计算、表单验证规则。
Example
Before
const calculateBonus = (level,salary) => {
switch(level){
case 's':{
return salary*4
}
case 'a':{
return salary*3
}
case 'b':{
return salary*2
}
default:{
return 0
}
}
}
calculateBonus('s',20000)
calculateBonus('a',10000)
After
const strategies = {
s: (salary) => salary * 4,
a: (salary) => salary * 3,
b: (salary) => salary * 2,
};
const calculateBonus = (level, salary) => strategies[level](salary);
calculateBonus("s", 20000);
calculateBonus("a", 10000);
优点
把策略和业务逻辑解耦,只要去维护策略就行了
代理模式
定义
为一个对象提供一个代用品或占位符,以便控制对它的访问。替身对象可对请求预先进行处理,再决定是否转交给本体对象。
应用场景
当我们不方便直接访问某个对象时,或不满足需求时,可考虑使用一个替身对象来控制对该对象的访问。
Example
Before
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");
After
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事件,消息通知。
Example
class PubSub {
constructor() {
this.subscribers = {};
}
subscribe(type, fn) {
const listeners = this.subscribers[type] || [];
listeners.push(fn);
}
unsubscribe(type, fn) {
const listeners = this.subscribers[type];
if (!listeners || !listeners.length) return;
this.subscribers[type] = listeners.filter((listener) => listener !== fn);
}
publish(type, ...args) {
const listeners = this.subscribers[type];
if (!listeners || !listeners.length) return;
listeners.forEach((fn) => fn(...args));
}
}
const ob = new PubSub();
ob.subscribe("add", (val) => console.log(val));
ob.publish("add", 1);
命令模式
定义
执行某些特定事情的指令
应用场景
富文本编辑器工具栏
Example
const setCommand = (button, command) => {
button.onclick = () => {
command.execute();
};
};
//业务逻辑:富文本编辑器菜单栏里有个刷新按钮,点击之后刷新文本内容
const MenuBar = {
refresh: () => {
console.log("refresh");
},
};
const RefreshMenuBarCommand = (receiver) => {
return {
execute: () => {
receiver.refresh();
},
};
};
const refreshMenuBarCommand = RefreshMenuBarCommand(MenuBar);
setCommand(refreshButton, refreshMenuBarCommand);
组合模式
定义
用小的子对象来构建更大的对象,将对象组合成树形结构,以表示“部分-整体的层次结构”
应用场景
从is-a到has-a
Example
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()
装饰器模式
定义
能够在不改变对象自身的基础上,在程序运行期间动态地添加职责
应用场景
数据上报,统计函数执行时间,中间件
Example
Before
const ajax = (type, url, param) => {
console.log(param);
};
const getToken = () => "Token";
ajax();
getToken();
After
//添加职责
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();
});
适配器模式
定义
解决两个软件实体间的接口不兼容问题,不需要改变已有接口,就能使他们协同作用(有点想转换器)
应用场景
接口不兼容的情况
Example
Before
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();
}
};
After
const aMap = {
show: () => {
console.log("开始渲染地图A");
},
};
const bMap = {
display: () => {
console.log("开始渲染地图B");
},
};
//适配层
const bMapAdapter = {
show: () => {
return bMap.display();
},
};
const renderMap = (map) => {
map.show instanceof Function && map.show();
};