享元模式
- 是一种用于性能优化的模式;
- 核心是运用共享技术来有效支持大量细粒度的对象,避免对象间拥有相同的内容造成多余的开销;
内部状态和外部状态
- 享元模式要求对象的属性划分为内部状态和外部状态;
- 目标是减少共享对象的数量;
- 内部状态存储于对象内部;
- 内部状态可以被一些对象共享;
- 内部状态独立于具体的场景,通常不会改变;
- 外部状态取决于具体的场景,并根据场景而变化,外部状态不能被共享;
- 这样可以吧所有内部状态相同的对象都指定为同一个共享的对象;而外部状态从对象身上剥离并存储在外部;
文件上传例子
var Upload = function( uploadType, fileName, fileSize ){ this.uploadType = uploadType; this.fileName = fileName; this.fileSize = fileSize; this.dom= null;};Upload.prototype.init = function( id ){ var that = this; this.id = id; this.dom = document.createElement( 'div' ); this.dom.innerHTML = '文件名称:'+ this.fileName +', 文件大小: ' + this.fileSize +'' + ''; this.dom.querySelector( '.delFile' ).onclick = function(){ that.delFile(); } document.body.appendChild( this.dom );};Upload.prototype.delFile = function(){ if(this.fileSize < 3000) return this.dom.parentNode.removeChild( this.dom ); if(window.confirm('确定要删除该文件吗? ' + this.fileName)) return this.dom.parentNode.removeChild( this.dom );}; var id = 0;window.startUpload = function( uploadType, files ){ //uploadType 区分是控件还是flash for(var i = 0, file; file = files[i++];){ var uploadObj = new Upload( uploadType, file.fileName, file.fileSize ); uploadObj.init( id++ ); // 给upload 对象设置一个唯一的id }};startUpload( 'plugin', [{ fileName: '1.txt', fileSize: 1000} , { fileName: '2.html', fileSize: 3000}]);startUpload( 'flash', [{ fileName: '3.txt', fileSize: 1000}]);
var Upload = function(uploadType){ this.uploadType = uploadType;};Upload.prototype.delFile = function(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);};//工厂实例化var UploadFactory = (function(){ var createdFlyWeightObjs = {}; return { create: function(uploadType){ if(createdFlyWeightObjs[uploadType]) return createdFlyWeightObjs[uploadType]; return createdFlyWeightObjs[uploadType] = new Upload(uploadType); } }})();//管理器:剥离和封装外部状态var uploadManager = (function(){ var uploadDatabase = {}; return { add: function(id, uploadType, fileName, fileSize){ var flyWeightObj = UploadFactory.create(uploadType); var dom = document.createElement('div'); dom.innerHTML = '文件名称:'+ fileName +', 文件大小: ' + fileSize +'' + ''; dom.querySelector('.delFile').onclick = function(){ flyWeightObj.delFile( id ); } document.body.appendChild(dom); uploadDatabase[ id ] = { fileName: fileName, fileSize: fileSize, dom: dom }; return flyWeightObj; }, setExternalState: function( id, flyWeightObj ){ var uploadData = uploadDatabase[id]; for (var i in uploadData) flyWeightObj[i] = uploadData[i]; } }})();var id = 0;window.startUpload = function(uploadType, files ){ for(var i = 0, file; file = files[i++]; ) var uploadObj = uploadManager.add(++id, uploadType, file.fileName, file.fileSize);};startUpload( 'plugin', [{ fileName: '1.txt', fileSize: 1000} , { fileName: '2.html', fileSize: 3000}]);startUpload( 'flash', [{ fileName: '3.txt', fileSize: 1000}]);
翻页的例子
var articles = []; for(var m = 0; m < 12; m++) { articles.push('这是第'+ m + '条信息') }; var Flyweight = function () { var created = []; function create() { var dom = document.createElement('div'); document.getElementById('container').appendChild(dom); created.push(dom); return dom; } return { getDiv: function () { if(created.length < 5) { return create(); } else { var div = created.shift(); created.push(div); return div; } } } }(); var pager = 0, num = 5, len = articles.length; for(var i = 0; i < 5; i++) { Flyweight.getDiv().innerHTML = articles[i]; } document.getElementById('next_page').onclick = function () { if(articles.length < 5) return; var n = ++pager * num % len, j = 0; for(; j < 5; j++) { if(articles[n + j]) { Flyweight.getDiv().innerHTML = articles[n + j]; } else if(articles[n + j - len]) { Flyweight.getDiv().innerHTML = articles[n + j - len]; } else { Flyweight.getDiv().innerHTML = ''; } } }
适用性
- 使用享元模式会多维护一个
factory
对象和一个manager
对象;所有一般以下情况比较适用享元模式: - 一个程序中使用了大量相似的对象,并造成很大的内存开销;
- 对象的大多数状态都可以变为外部状态,剥离外部状态后可以用相对少的贡献对象取代;
职责链模式
- 使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系;将这些对象连城一条链,并沿着这条链传递请求,直到有一个对象处理它为止;
实际开发中的一个例子
- 变量
orderType
:订单的级别,分别为1,2,3; pay
:是否支付过定金,false
的话统一降到3; stock
:库存量,1,2级别不受此此限制;
- 初级代码的话会用大量
if-else
来判断分支;这样耦合度很大且不利于以后修改和维护;使用职责链修改
var order500 = function(orderType, pay, stock) { if(orderType === 1 && pay === true) { console.log('1级订单,已支付'); } else { return 'nextSuccessor'; }};var order200 = function(orderType, pay, stock) { if(orderType === 2 && pay === true) { console.log('2级订单,已支付'); } else { return 'nextSuccessor'; }};var orderNormal = function(orderType, pay, stock) { if(stock > 0) { console.log('普通购买'); } else { console.log('库存不足'); }};var Chain = function (fn) { this.fn = fn; this.successor = null;};Chain.prototype.setNextSuccesor = function (successor) { return this.successor = successor;};Chain.prototype.passRequest = function() { var ret = this.fn.apply(this, arguments); if(ret === 'nextSuccessor') return this.successor && this.successor.passRequest.apply(this.successor, arguments); return ret;};var ChainOrder500 = new Chain(order500);var ChainOrder200 = new Chain(order200);var chainOrderNormal = new Chain(orderNormal);ChainOrder500.setNextSuccesor(ChainOrder200);ChainOrder200.setNextSuccesor(chainOrderNormal);ChainOrder500.passRequest(1, true, 1000);ChainOrder500.passRequest(2, true, 1000);ChainOrder500.passRequest(3, true, 1000);ChainOrder500.passRequest(1, false, 0);
异步的职责链
Chain.prototype.next = function() { return this.successor && this.successor.passRequest.apply(this.successor, arguments);};//测试var fn1 = new Chain(function() { console.log(1); return 'nextSuccessor';})var fn2 = new Chain(function() { console.log(2); var self = this; setTimeout(function() { self.next(); }, 1000)});var fn3 = new Chain(function() { console.log(3);});fn1.setNextSuccesor(fn2).setNextSuccesor(fn3);fn1.passRequest();
用AOP实现职责链
Function.prototype.after = function(fn) { var self = this; return function() { var ret = self.apply(this, arguments); if(ret = 'nextSuccessor') return fn.apply(this, arguments); }};var oredr = order500.after(order200).after(orderNormal);order(1, true, 1000);order(2, true, 1000);order(1, fasle, 1000);
- 这种把函数叠在一起的方法同时也增加了函数的作用域;如果链条太长的话,也会对性能有较大的影响;
另一个例子
//请求模块var sendData = function (data, dealType, dom) { .... xhr.onload = function (event) { if((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) { dealData(xhr.responseText, dealType, dom); } else { //... } .... } ....};//适配响应数据var dealData(data, dealType, dom) { .... switch(dealType) { case 'sug': .... createSug(data, dom); case 'invalidate': .... createInvalidate(data, dom); } ....}//组件模块var createSug(data, dom) { ....}var createInvalidate(data, dom) { ....}
- 在半成品方案情况下可以设置如此的责任链模式;
- 耦合之间进行单元测试也更加简单;
职责链的优缺点
- 优点
- 解耦了请求发送者和多个接受者之间的关系;只需把请求传递给第一个节点;
- 链中的节点对象可以灵活地拆分重组;
- 可以手动设定起始节点;
- 缺点
- 由于不能确定每个节点都会正常处理;应该在链尾增加一个保底的接受者节点来处理即将离开链尾的请求;
- 从性能考虑,应该避免过长的职责链带来的性能消耗;