设计模式详解(二)-- 结构型设计模式
阅读目录
享元模式
概念:运用共享技术来有效支持大量细粒度的对象,避免对象间拥有相同内容造成多余的开销; 将数据和方法分成内部数据、内部方法和外部数据、外部方法,实现内部高内聚,外部低耦合
应用创景:适合大量创建重复类的场景,使用该模式,减少内存消耗
项目案例:
class ObjectPool { constructor() { this._pool = []; // } // 创建对象 create(Obj) { return this._pool.length === 0 ? new Obj(this) // 对象池中没有空闲对象,则创建一个新的对象 : this._pool.shift(); // 对象池中有空闲对象,直接取出,无需再次创建 } // 对象回收 recover(obj) { return this._pool.push(obj); } // 对象池大小 size() { return this._pool.length; } } // 模拟文件对象 class File { constructor(pool) { this.pool = pool; } // 模拟下载操作 download() { console.log(`+ 从 ${this.src} 开始下载 ${this.name}`); setTimeout(() => { console.log(`- ${this.name} 下载完毕`); // 下载完毕后, 将对象重新放入对象池 this.pool.recover(this); }, 100); } } let objPool = new ObjectPool(); let file1 = objPool.create(File); file1.name = "文件1"; file1.src = "https://download1.com"; file1.download(); let file2 = objPool.create(File); file2.name = "文件2"; file2.src = "https://download2.com"; file2.download(); setTimeout(() => { let file3 = objPool.create(File); file3.name = "文件3"; file3.src = "https://download3.com"; file3.download(); }, 200); setTimeout( () => console.log( `${"*".repeat(50)}\n下载了3个文件,但其实只创建了${objPool.size()}个对象` ), 1000 );
输出结果
+ 从 https://download1.com 开始下载 文件1 test.html:48 + 从 https://download2.com 开始下载 文件2 test.html:50 - 文件1 下载完毕 test.html:50 - 文件2 下载完毕 test.html:48 + 从 https://download3.com 开始下载 文件3 test.html:50 - 文件3 下载完毕 test.html:72 ************************************************** 下载了3个文件,但其实只创建了2个对象
代理模式
概念:代理模式是一种对程序对象进行控制性访问的一类解决方案;保护代理--在代理中直接拒绝对对象的访问;虚拟代理--客户程序需要通过这个虚拟代理来调用本体对象的方法;
缓存代理--为一些开销大的运算结果提供暂时存储,若参数一致,直接返回结果
项目案例--保护代理
// 定义一个广告类 var Ad = function(price){ this.price = price; }; Ad.prototype.getPrice = function() { return this.price; }; // 定义一个助理对象 var assistant = { init: function(ad) { var money = ad.getPrice(); if(money > 300) { this.receiveAd(money); } else { this.rejectAd(); } }, receiveAd: function(price) { star.receiveAd(price); }, rejectAd: function() { star.rejectAd(); } }; // 定义一个明星对象 var star = { receiveAd: function(price) { console.log('广告费' + price + '万元'); }, rejectAd: function() { console.log('拒绝小制作!'); } }; assistant.init(new Ad(5)); // "拒绝小制作!" assistant.init(new Ad(500)); // "广告费500万元"
项目案例--虚拟代理
var myImage = (function() { var imgNode = document.createElement('img'); document.body.appendChild(imgNode); return { setSrc: function(src) { imgNode.src = src; } } })(); var preImage = (function() { var img = new Image; img.onload = function() { myImage.setSrc(img.src); }; return { setSrc: function(src) { myImage.setSrc( 'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1562820121166&di=ba2e876afd8d9937527f42dcb68bb7fb&
imgtype=0&src=http%3A%2F%2Fp3.so.qhmsg.com%2Ft01110474ea13919dc8.gif'); img.src = src; } } })(); preImage.setSrc('https://cn.bing.com/az/hprichbg/rb/TadamiTrain_ZH-CN13495442975_1920x1080.jpg');
桥接模式
概念:通过使用封装、聚合及继承等行为让不同的类承担不同的职责;特点--抽象与行为实现分离,保持各部分的独立性以及应对他们的功能扩展
特点:1、实现了抽象和实现部分的分离;2、更好的可扩展性;3、可动态的切换实现;4实现细节对客户端透明,可以对用户隐藏实现细节;
5、桥接模式的引入增加了系统的理解和设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计和编程
项目案例--模拟forEach方法
const forEach = (arr, callback) => { if (!Array.isArray(arr)) return; const length = arr.length; for (let i = 0; i < length; ++i) { callback(arr[i], i); } }; // 以下是测试代码 let arr = ["a", "b"]; forEach(arr, (el, index) => console.log("元素是", el, "位于", index));
项目案例--模拟事件监听
addEvent(element, 'click', getBeerByIdBridge); function getBeerByIdBridge (e) { getBeerById(this.id, function(beer) { console.log('Requested Beer: '+beer); }); } //getBeerByIdBridge将抽象的click事件和getBeerById连接,将事件源的ID,以及自定义的call函数作为参数传入到getBeerById函数里
组合模式
概念:将对象组合成树形结构以表示“部分-整体”的层次结构,组合模式使得用户对单个对象和组合对象的使用具有一致性
应用场景:1、电商网站中的产品订单,每一张产品订单可能有多个子订单组合;2、操作系统的文件夹,每个文件夹有多个子文件夹或文件,执行复制,删除操作相同
项目案例:子对象--产品子订单,总对象--产品总订单
function FlightOrder() {} FlightOrder.prototype.create = function () { console.log("flight order created"); } function HotelOrder() { } HotelOrder.prototype.create = function () { console.log("hotel order created"); } function TotalOrders() { this.orderList = []; } TotalOrders.prototype.addOrder = function (order) { this.orderList.push(order); } TotalOrders.prototype.create = function (order) { for (var i = 0, length = this.orderList.length; i < length; i++) { this.orderList[i].create(); } } var flight = new FlightOrder(); flight.create(); var orders = new TotalOrders(); orders.addOrder(new FlightOrder()); orders.addOrder(new HotelOrder()); orders.create();
装饰者模式
概念:在不改变对象自身的基础上,动态地添加功能代码
特点:灵活度高,代码耦合度低
项目案例--扩展input的focus,blur方法
/*装饰者*/ var decorator=function(input,focusFn,blurFn){ //获取事件源 var input=document.getElementById(input); //判断事件源是否绑定focus事件 if(typeof input.onfocus === 'function'){ //缓存事件源原有回调函数 var oldFocusFn = input.onfocus; //为事件源定义新的事件 input.onfocus = function(){ //事件源原有回调函数 oldFocusFn(); //新增回调函数 focusFn(); } }else{ //事件源未绑定事件,直接为事件源添加新增回调函数 input.onfocus = focusFn; } //判断事件源是否绑定blur事件 if(typeof input.onblur === 'function'){ //缓存事件源原有回调函数 var oldBlurFn = input.onblur; //为事件源定义新的事件 input.onblur = function(){ oldBlurFn(); blurFn(); } }else{ //事件源未绑定事件,直接为事件源添加新增回调函数 input.onblur = blurFn; } }
优化之后代码
var decorator1 = function(id,type,fn){ var dom = typeof id === 'string' ? document.getElementById(id) : id; //判断事件源是否绑定事件 if(typeof dom[type] === 'function'){ //缓存事件源原有回调函数 var oldFn = dom[type]; //为事件源定义新的事件 dom[type] = function(){ //事件源原有回调函数 oldFn(); //新增回调函数 fn(); } }else{ //事件源未绑定事件,直接为事件源添加新增回调函数 dom[type] = fn; } };
适配器模式
概念:将一个类(对象)的接口(方法或属性)转化成另一个接口,为多个不兼容接口之间提供“转化器”
项目案例--es6实现
const API = { qq: () => ({ n: "菊花台", a: "周杰伦", f: 1 }), netease: () => ({ name: "菊花台", author: "周杰伦", f: false }) }; const adapter = (info = {}) => ({ name: info.name || info.n, author: info.author || info.a, free: !!info.f }); console.log(adapter(API.qq()));//{name: "菊花台", author: "周杰伦", free: true} console.log(adapter(API.netease()));//{name: "菊花台", author: "周杰伦", free: false}
参考网站:
https://xin-tan.com
https://www.cnblogs.com/jtnote/p/5988795.html
https://www.jianshu.com/p/798152197124
浙公网安备 33010602011771号