设计模式概略 + 常用设计模式 包括单例模式、策略模式、代理模式、发布订阅模式、命令模式、组合模式、装饰器模式、适配器模式
定义:在软件设计过程中,针对特定问题的简洁而优雅的解决方案
定义:唯一 & 全局访问。保证一个类仅有一个实例,并提供一个访问它的全局访问点。
应用场景: 能被缓存的内容,例如登陆弹窗。
定义:定义一系列的算法,把他们一个个封装起来,并且使他们可以相互替换。把看似毫无联系的代码提取封装、复用,使之更容易被理解和扩展。
应用场景:要完成一件事情,有不同的策略。例如绩效计算、表单验证规则。
示例:
使用前
// 策略
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);
参考: 字节青训营课程