文章目录
前言
借着这段时间拜读了曾探大神的《JavaScript设计模式与开发实践》一书。本文以此总结一下JS常见的设计模式与实现方法,主要做一些笔记以方便自己过后复习与加深理解,同时也希望把书中典型例子整理出来和大家分享,共同探讨和进步。
设计模式:在面向对象软件设计过程中针对特定问题的简洁而优雅地解决方案。
一、单例模式
定义:保证一个类仅有一个实例,并提供一个访问它的全局访问点。例如:线程池、全局缓存、window对象等。
核心逻辑:一个变量来标志是否创建对象,如果是,返回该创建好的对象。
将创建对象和管理单例的职责分布在两个不同的方法中。
let obj;
if (obj) {
obj = xxxxx;
}
例子:
// 核心逻辑
const getSingle = function( fn ) {
let result;
return function() {
return result || ( result = fn.apply(this, arguments) );
}
};
class Singletion {
constructor(name) {
this.name = name;
}
getName() {
alert(this.name);
}
}
const singletion = getSingle(function(name) {
let sing = new Singletion(name);
return sing;
});
singletion('a').getName();
singletion('b').getName();
二、策略模式
定义:定义一系列的算法,把它们一个个封装起来,并且使它们可以相互替换。
核心逻辑:将算法的使用与算法的实现分离开来。
一个基于策略模式的程序至少有两个部分组成。第一个部分是一组策略类,封装了具体的算法,
并负责具体的计算过程。第二个部分是环境类,接收客户的请求,随后把请求委托给某一个策略类。
例子一:
const strategies = {
"S" : function(salary){
return salary * 4;
},
"A" : function(salary){
return salary * 3;
},
"B" : function(salary){
return salary * 2;
}
};
const calculateBonus = function(level, salary){
return strategies[level](salary);
}
console.log(calculateBonus("S", 2000));
例子二:
<html>
<body>
<form action="" method="post" id="myform">
请输入用户名:<input type="text" name="userName" />
<button>提交</button>
</form>
</body>
<script>
// 策略类
const strategies = {
isNonEmpty: function(value, errorMsg) {
if(value === ''){
return errorMsg;
}
},
minLength: function(value, length, errorMsg) {
if(value.length < length) {
return errorMsg;
}
},
isMobile: function(value , errorMsg) {
if(!/(^1[3|5|8][0-9]{9}$)/.test(value)) {
return errorMsg;
}
}
};
// 类
class Validator {
constructor() {
this.cache = [];
}
add(dom, rules) {
for(let i = 0, rule; rule = rules[i++];) {
const strategyAry = rule.strategy.split(':');
const errorMsg = rule.errorMsg;
this.cache.push(function(){
const strategy = strategyAry.shift();
strategyAry.unshift(dom.value);
strategyAry.push(errorMsg);
return strategies[strategy].apply(dom, strategyAry);
})
}
}
start() {
for(let i = 0, validatorFunc; validatorFunc = this.cache[i++];) {
const errorMsg = validatorFunc();
if(errorMsg) {
return errorMsg;
}
}
}
}
// 客户端调用
const myform = document.getElementById('myform');
const vaildataFun = function() {
const validator = new Validator();
validator.add(myform.userName, [
{
strategy: 'isNonEmpty',
errorMsg: '用户名不能为空',
},
{
strategy: 'minLength:10',
errorMsg: '用户名长度不能小于10位',
}
]);
const errorMsg = validator.start();
return errorMsg;
};
myform.onsubmit = function(e) {
const errorMsg = vaildataFun();
if(errorMsg) {
alert(errorMsg);
return false;
}
}
</script>
</html>
总结:
- 利用组合、委托和多态等技术和思想,可以有效地避免多重条件选择语句。
- 提供了对开放-封闭原则的完美支持,将算法封装在独立的 strategy 中,使得它们易于理解,易于扩展。
- 其中的算法也可以复用在系统的其他地方,从而避免许多重复的复制粘贴工作。
- 利用组合和委托来让环境类拥有执行算法的能力,这也是继承的一种更方便的替代方案。
三、代理模式
定义:为一个对象提供一个代用品或占位符,以便控制对它的访问(如 保护代理、虚拟代理、缓存代理、防火墙代理、远程代理、写时复制代理等)。
- 代理模式符合开放封闭原则
- 本体对象和代理对象拥有相同的方法,在用户看来并不知道请求的本体对象还是代理对象。
例子一(虚拟代理):
const miniConsole = (function() {
const cache = [];
const handler = function(e) {
if(e.keyCode === 113) {
const script = document.createElement('script');
script.onload = function() {
for(let i = 0, fn; fn = cache[i++];){
fn();
}
};
script.src = 'miniConsole.js';
document.getElementsByTagName('head')[0].appendChild(script);
document.body.removeEventListener('keydown', handler);
}
};
document.body.addEventListener('keydown', handler, false);
return {
log: function(){
const args = arguments;
cache.push(function() {
return miniConsole.log.apply(miniConsole, args);
});
}
}
})();
miniConsole.log(11);
例子二(缓存代理):
// 计算乘积
const mult = function() {
let a = 1;
for( let i = 0, l = arguments.length; i < l; i++){
a = a * arguments[i];
}
return a;
};
// 计算加和
const plus = function() {
let a = 1;
for( let i = 0, l = arguments.length; i < l; i++){
a = a + arguments[i];
}
return a;
}
// 创建缓存代理工厂
const createProxyFactory = function(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),
proxyPlus = createProxyFactory(plus);
proxyMult(1, 2, 3);
proxyPlus(1, 2, 3);
四、迭代器模式
定义:提供一种方法顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象的内部表示。
迭代器模式可以把迭代的过程从业务逻辑中分离出来,在使用迭代器模式之后,即使不关心对象的内部构造,也可以顺利访问其中的每个元素。
// 简单的一个迭代器
class Iterator {
constructor(obj) {
this.current = 0;
this.obj = obj;
}
next() {
this.current += 1;
}
isDone() {
return this.current >= this.obj.length;
}
getCurrItem() {
return this.obj[this.current];
}
}
const iterator = new Iterator([1,2,3,4]);
while (!iterator.isDone()) {
console.log(iterator.getCurrItem())
iterator.next()
}
例子:可以用于某些特殊结构迭代检查,如检测 js 某个功能在浏览器是否兼容性等
const getHuohu = function(){
console.log('检测在火狐中是否可以执行');
return false;
}
const getGuge = function(){
console.log('检测在谷歌中是否可以执行');
return true;
}
const iteratorObj = function(){
for(let i = 0, fn; fn = arguments[i++];){
let result = fn();
if(result !== false){
return result;
}
}
}
iteratorObj(getHuohu, getGuge)
五、发布-订阅模式(观察者模式)
定义:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知。
发布—订阅模式:
class Event{
constructor(){
this.clientList = {};
}
listen(key, fn){
if(!this.clientList[key]){
this.clientList[key] = [];
}
this.clientList[key].push(fn);
}
trigger(){
const key = Array.prototype.shift.call(arguments),
fns = this.clientList[key];
if(!fns || fns.length === 0){
return false;
}
for(let i = 0, fn; fn = fns[i++];){
fn.apply(this, arguments);
}
}
remove(key, fn){
const fns = this.clientList[key];
if(!fns){
return false;
}
if(!fn) {
// 订阅类型的事件全部取消
fns && (fns.length = 0);
}else {
for( let l = fns.length - 1; l >= 0; l--){
let _fn = fns[l];
if( _fn === fn){
fns.splice(l, 1);
}
}
}
}
}
let event = new Event();
event.listen( 'squareMeter88', function( price ){ // 小红订阅消息
console.log( '价格= ' + price ); // 输出:'价格=2000000'
});
event.trigger( 'squareMeter88', 2000000 );
六、命令模式
定义:在软件系统中,“行为请求者”与“行为实现者”通常呈现一种“紧耦合”。但在某些场合,比如要对行为进行“记录、撤销/重做、事务”等处理,这种无法抵御变化的紧耦合是不合适的。在这种情况下,如何将“行为请求者”与“行为实现者”解耦?将一组行为抽象为对象,实现二者之间的松耦合。使得请求发送者和请求接收者能够消除彼此之间的耦合关系。
简单命令模式:
const closeDoorCommand = {
execute: function() {
// something
}
};
例子:点击按钮之后,必须项某些负责具体行为的对象发送请求,这些对象就是请求的接收者。但是目前并不知道接收者是什么对象,也不知道接收者究竟会做什么。借助命令类的帮助,以便解开按钮和负责具体行为之间的耦合。
<!DOCTYPE html>
<html lang="en">
<body>
<div>
<button id="button1">点击按钮1</button>
<button id="button2">点击按钮2</button>
<button id="button1">点击按钮3</button>
</div>
<script>
const btn1 = document.getElementById('button1');
const btn2 = document.getElementById('button2');
const btn3 = document.getElementById('button3');
// 发送请求
class Executor {
setCommand(button, command) {
button.onclick = function() {
command.execute();
}
}
}
class Menu {
add() {
console.log('增加子菜单');
}
del() {
console.log('删除子菜单');
}
}
// 命令类
class AddMenu {
constructor(receiver) {
this.receiver = receiver;
}
execute() {
this.receiver.add();
}
}
class DelMenu {
constructor(receiver) {
this.receiver = receiver;
}
execute() {
this.receiver.del();
}
}
const executor = new Executor();
const menu = new Menu();
const addMenu = new AddMenu(menu);
const delMenu = new DelMenu(menu);
executor.setCommand(btn1, addMenu);
executor.setCommand(btn2, addMenu);
</script>
</body>
</html>
命令模式还支持撤销、重做、排队等操作。
七、组合模式
定义:在软件系统中,“行为请求者”与“行为实现者”通常呈现一种“紧耦合”。但在某些场合,比如要对行为进行“记录、撤销/重做、事务”等处理,这种无法抵御变化的紧耦合是不合适的。在这种情况下,如何将“行为请求者”与“行为实现者”解耦?将一组行为抽象为对象,实现二者之间的松耦合。使得请求发送者和请求接收者能够消除彼此之间的耦合关系。
组合模式将对象组合成树形结构,以表示“部分-整体”的层次结构。另一个好处是通过对象的多态性表现,使得用户对单个对象和组合对象的使用具有一致性。
例如一个万能遥控器里面添加一个命令的时候,并不关心这个命令是宏命令还是普通子命令。我们只需要确定它是一个命令,并且这个命令拥有可执行的execute方法,那么这个命令就可以被添加进万能遥控器。
class MacroCommand {
constructor() {
this.commandsList = [];
}
add(command) {
this.commandsList.push(command);
}
execute(){
for(let i = 0, command; command = this.commandsList[i++]; ) {
command.execute();
}
}
}
class openAcCommand {
execute() {
console.log('打开空调');
}
add() {
throw new Error('叶对象不能添加子节点');
}
}
class openTvCommand {
execute() {
console.log('打开电视');
}
add() {
throw new Error('叶对象不能添加子节点');
}
}
const macroCommand = new MacroCommand();
macroCommand.add(new openAcCommand());
macroCommand.add(new openTvCommand());
macroCommand .execute();
// 打开空调
// 打开电视
例子:扫描文件夹
class Folder{
constructor(name) {
this.name = name;
this.parent = null;
this.files = [];
}
add(file) {
file.parent = this;
this.files.push(file);
}
scan() {
console.log('开始扫描文件夹:' + this.name);
for( let i = 0, file, files = this.files; file = files[i++];){
file.scan();
}
}
remove() {
if(!this.parent) {
return;
}
for( let files = this.parent.files, l = files.length - 1; l >= 0; l--){
const file = files[l];
if(file == this) {
files.splice(l, 1);
}
}
}
}
class File {
constructor(name) {
this.name = name;
this.parent = null;
}
add(file) {
throw new Error('不能添加在文件下面');
}
scan() {
console.log('开始扫描文件夹:' + this.name);
}
remove() {
if(!this.parent) {
return;
}
for( let files = this.parent.files, l = files.length - 1; l >= 0; l--){
const file = files[l];
if(file == this) {
files.splice(l, 1);
}
}
}
}
const folder = new Folder('学习资料');
const folder1 = new Folder('JavaScript');
const file1 = new File('node.js');
folder1.add( new File('宝可梦'));
folder.add(folder1);
folder.add(file1);
folder1.remove();
folder.scan();
八、模板方法模式
定义:是一种只需使用继承就可以实现的非常简单模式。由两部分结构组成,第一个部分是一个抽象父类,第二个部分是具体实现的子类。通常在抽象父类中封装了子类的算法框架,包括实现一些公共方法以及封装子类中所有方法的执行顺序。子类通过继承这个抽象类,也继承了整个算法结构,并且可以选择重写父类的方法。
抽象类不能被实例化,一定是用来被某些具体类继承。父类规定了子类的方法和执行这些方法的顺序,子类就应该拥有这些方法,并且提供正确的实现。
class Beverage {
// 模板方法
init() {
this.boilWater();
this.brew();
this.pourInCup();
if ( this.customerWantsCondiments() ){ // 如果挂钩返回 true,则需要调料
this.addCondiments();
}
}
boilWater() {
console.log( '把水煮沸' );
}
brew() {
throw new Error( '子类必须重写 brew 方法' );
}
pourInCup() {
throw new Error( '子类必须重写 pourInCup 方法' );
}
addCondiments() {
throw new Error( '子类必须重写 addCondiments 方法' );
}
// 钩子
customerWantsCondiments() {
return true;
}
}
class Coffee extends Beverage {
brew() {
console.log( '用沸水冲泡咖啡' );
}
pourInCup() {
console.log( '把咖啡倒进杯子' );
}
addCondiments() {
console.log( '加糖和牛奶' );
}
customerWantsCondiments() {
return window.confirm( '请问需要调料吗?' );
}
}
let coffee = new Coffee();
coffee.init();
好莱坞原则:允许底层组件将自己挂钩到高层组件中,而高层组件会决定什么时候、以何种方式去使用这些底层组件(子类放弃了对自己控制权,而是改为父类通知子类)。常用于其他模式和场景:例如发布-订阅模式和回调函数。
九、享元模式
定义:是一种用于性能优化的模式。运用共享技术来有效支持大量细粒度的对象。
将对象属性划分为内部状态与外部状态,尽量减少共有对象的数量。
- 内部状态存储于对象内部
- 内部状态可以被一些对象共享
- 内部状态独立于具体的场景,通常不会改变
- 外部状态取决于具体的场景,并根据场景而变化,外部状态不能被共享
以下情况发生时便可以使用享元模式:
- 一个程序中使用了大量的相似对象
- 由于使用了大量对象,造成很大的内存开销
- 对象的大多数状态都可以变为外部状态
- 剥离出对象的外部状态之后,可以用相对较少的共享对象取代大量对象
例子一(文件上传):
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body></body>
<script>
// 剥离外部状态
// uploadType为内部状态
class Upload {
constructor(uploadType) {
this.uploadType = uploadType;
}
delFile(id) {
uploadManager.setExternalState(id, this);
if (this.fileSize < 3000) {
return this.dom.parentNode.removeChild(this.dom);
}
if (window.confirm("确定要删除改文件吗?" + this.fileName)) {
return this.dom.parentNode.removeChild(this.dom);
}
}
}
// 工厂模式
const UploadFactory = (function () {
const createdFlyWeightObjs = {};
return {
create(uploadType) {
if (createdFlyWeightObjs[uploadType]) {
return createdFlyWeightObjs[uploadType];
}
return (createdFlyWeightObjs[uploadType] = new Upload(uploadType));
},
};
})();
const uploadManager = (function () {
const uploadDatabase = {};
return {
add(id, uploadType, fileName, fileSize) {
const flyWeightObj = UploadFactory.create(uploadType);
const dom = document.createElement("div");
dom.innerHTML = `<div>文件名称:${fileName},文件大小:${fileSize}。<button class='delFile'>删除</button></div>`;
dom.querySelector(".delFile").onclick = function () {
flyWeightObj.delFile(id);
};
document.body.appendChild(dom);
uploadDatabase[id] = {
fileName,
fileSize,
dom,
};
return flyWeightObj;
},
setExternalState(id, flyWeightObj) {
const uploadData = uploadDatabase[id];
for (let i in uploadData) {
flyWeightObj[i] = uploadData[i];
}
},
};
})();
let id = 0;
window.startUpload = function (uploadType, files) {
for (let i = 0, file; (file = files[i++]); ) {
const uploadObj = uploadManager.add(
++id,
uploadType,
file.fileName,
file.fileSize
);
}
};
startUpload("plugin", [
{
fileName: "1.text",
fileSize: 1000,
},
{
fileName: "2.html",
fileSize: 1000,
},
]);
startUpload("flash", [
{
fileName: "3.text",
fileSize: 1000,
},
{
fileName: "4.html",
fileSize: 5000,
},
]);
</script>
</html>
例子二(对象池):
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body></body>
<script>
const objectPoolFactory = function(createObjFn){
const objectPool = [];
return {
create(){
let obj = objectPool.length === 0 ? createObjFn.apply(this.arguments) : objectPool.shift();
return obj;
},
recover(obj){
objectPool.push(obj);
}
}
}
const iframeFactory = objectPoolFactory(function(){
let iframe = document.createElement('iframe');
document.body.appendChild(iframe);
iframe.onload = function(){
iframe.onload = null;
iframeFactory.recover(iframe);
}
return iframe;
});
const iframe1 = iframeFactory.create();
iframe1.src = 'http://www.baidu.com';
const iframe2 = iframeFactory.create();
iframe2.src = 'http://www.baidu.com';
</script>
</html>
十、职责链模式
定义:使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系,将这些对象连成一条链,并沿着这条链传递该请求,知道有一个对象处理它为止。
请求发送者只需要知道链中的第一个节点,从而弱化了发送者和一组接收者之间的强联系。
例子:
// 异步职责链
class Chain {
constructor(fn) {
this.fn = fn;
// 下一个节点
this.successor = null;
}
setNextSuccess(successor) {
return (this.successor = successor);
}
passRequest() {
const ret = this.fn.apply(this, arguments);
if (ret === "nextSuccessor") {
return (
this.successor &&
this.successor.passRequest.apply(this.successor, arguments)
);
}
return ret;
}
// 异步,手动执行
next() {
return (
this.successor &&
this.successor.passRequest.apply(this.successor, arguments)
);
}
}
const f1 = new Chain(function () {
console.log(1);
return "nextSuccessor";
});
const f2 = new Chain(function () {
console.log(2);
setTimeout(() => {
this.next();
}, 3000);
});
const f3 = new Chain(function () {
console.log(3);
});
f1.setNextSuccess(f2).setNextSuccess(f3);
f1.passRequest();
十一、中介者模式
定义:解除对象与对象之间的紧耦合关系。所有的相关对象都通过中介者对象来通信,而不是互相引用,所以当一个对象发生改变时,只需要通知中介者对象即可。
缺点是会增加一个中介者对象,因为对象之间交互的复杂性,转移成了中介者对象的复杂性,使得中介者经常是巨大的。一般来说,如果对象之间的复杂耦合确实导致调用和维护出现了困难,而且这些耦合度随项目的变化呈指数增长曲线,那么就可以考虑用中介者模式来重构代码。
例子(泡泡堂):
class Player {
constructor(name, teamColor) {
this.name = name;
this.teamColor = teamColor;
this.state = "alive";
}
win() {
console.log(`${this.name} won`);
}
lose() {
console.log(`${this.name} lost`);
}
die() {
this.state = "dead";
playerDirector.ReceiveMessage("playerDead", this);
}
remove() {
this.state = "dead";
playerDirector.ReceiveMessage("removePlayer", this);
}
changeTeam(color) {
playerDirector.ReceiveMessage("changeTeam", this, color);
}
}
const playerDirector = (function () {
const players = {};
const operations = {};
operations.addPlayer = function (player) {
const teamColor = player.teamColor;
players[teamColor] = players[teamColor] || [];
players[teamColor].push(player);
};
operations.removePlayer = function (player) {
const teamColor = player.teamColor;
const teamPlayers = players[teamColor] || [];
for (let i = teamPlayers.length - 1; i >= 0; i--) {
if (teamPlayers[i] === player) {
teamPlayers.splice(i, 1);
}
}
};
operations.changeTeam = function (player, newTeamColor) {
operations.removePlayer(player);
player.teamColor = newTeamColor;
operations.addPlayer(player);
};
operations.playerDead = function (player) {
const teamColor = player.teamColor;
const teamPlayers = players[teamColor];
let all_dead = true;
for (let i = 0, player; (player = teamPlayers[i++]); ) {
if (player.state !== "dead") {
all_dead = false;
break;
}
}
if (all_dead) {
for (let i = 0, player; (player = teamPlayers[i++]); ) {
player.lose();
}
for (let color in players) {
if (color !== teamColor) {
let teamPlayers = players[color];
for (let i = 0, player; (player = teamPlayers[i++]); ) {
player.win();
}
}
}
}
};
const ReceiveMessage = function () {
const message = Array.prototype.shift.call(arguments);
operations[message].apply(this, arguments);
};
return {
ReceiveMessage,
};
})();
const playerFactory = function (name, teamColor) {
const newPlayer = new Player(name, teamColor);
playerDirector.ReceiveMessage("addPlayer", newPlayer);
return newPlayer;
};
const player1 = playerFactory("小白", "red");
const player2 = playerFactory("小红", "red");
const player3 = playerFactory("小明", "red");
const player4 = playerFactory("小花", "red");
const player5 = playerFactory("悟能", "blue");
const player6 = playerFactory("悟空", "blue");
const player7 = playerFactory("八戒", "blue");
const player8 = playerFactory("唐*", "blue");
player1.changeTeam("blue");
player1.die();
player2.die();
player3.die();
player4.die();
十二、装饰者模式
定义:在不改变对象自身的基础上,在程序运行期间给对象动态地添加职责。
将一个对象嵌入另一个对象之中,实际上相当于这个对象被另一个对象包装起来,形成一条包装链。请求随着这条链依次传递到所有的对象,每个对象都有处理这条请求的机会。
例子:
Function.prototype.after = function (fn) {
var _self = this;
return function () {
console.log(_self, this)
var result = _self.apply(this, arguments);
fn.apply(this, arguments);
return result;
};
};
Function.prototype.before = function (beforefn) {
var __self = this;
return function () {
beforefn.apply(this, arguments);
return __self.apply(this, arguments);
}
}
document.getElementById = document.getElementById.before(function () {
alert(1);
});
document.getElementById('button');
十三、状态模式
定义:允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类。区分事物内部的状态,事物内部状态的改变往往会带来事物的行为改变。
把事物的每种状态都封装成单独的类,跟此种状态有关的行为都被封装在这个类的内部。
例子一(开关):
class OffLightState {
constructor(light) {
this.light = light;
}
buttonWasPressed() {
console.log('弱光');
this.light.setState(this.light.weakLightState);
}
}
class WeakLightState {
constructor(light) {
this.light = light;
}
buttonWasPressed() {
console.log('强光');
this.light.setState(this.light.offLightState);
}
}
class Light {
constructor() {
this.offLightState = new OffLightState(this);
this.weakLightState = new WeakLightState (this);
this.currState = null;
}
init() {
const self = this;
this.currState = this.offLightState;
}
setState(newState) {
this.currState = newState;
}
click() {
this.currState.buttonWasPressed();
}
}
const light = new Light();
light.init();
light.click();
十四、适配器模式
定义:又名包装器,作用是解决两个软件实体间的接口不兼容的问题。使用适配器模式之后,原本由于接口不兼容而不能工作的两个软件实体可以一起工作。
- 适配器模式主要用来解决两个已有接口之间不匹配的问题,它不考虑这些接口是怎样实现的,也不考虑它们将来可能会如何演化。适配器模式不需要改变已有的接口,就能够使它们协同作用
- 装饰者模式和代理模式也不会改变原有对象的接口,但装饰者模式的作用是为了给对象增加功能。装饰者模式常常形成一条长的装饰链,而适配器模式通常只包装一次。代理模式是为了控制对对象的访问,通常也只包装一次
- 外观模式的作用倒是和适配器比较相似,有人把外观模式看成一组对象的适配器,但外观模式最显著的特点是定义了一个新的接口
例子:
const googleMap = {
show: function() {
console.log('开始渲染谷歌地图');
}
}
const baiduMap = {
display: function() {
console.log('开始渲染百度地图');
}
}
const baiduMpaAdapter = {
show: function() {
return baiduMap.display();
}
}
const renderMap = function(map) {
if(map.show instanceof Function) {
map.show();
}
}
renderMap(googleMap);
renderMap(baiduMpaAdapter)
总结
- 单一职责原则(SRP):一个对象(方法)只做一件事情
- 最少知识原则(LKP):一个软件实体应当尽可能少地与其他实体发生相互作用
- 开放-封闭原则(OCP):软件实体(类、模块、函数)等应该是可以扩展的,但是不可修改