06享元、责任链

享元模式

  • 是一种用于性能优化的模式;
  • 核心是运用共享技术来有效支持大量细粒度的对象,避免对象间拥有相同的内容造成多余的开销;

内部状态和外部状态

  • 享元模式要求对象的属性划分为内部状态和外部状态;
  • 目标是减少共享对象的数量;
    • 内部状态存储于对象内部;
    • 内部状态可以被一些对象共享;
    • 内部状态独立于具体的场景,通常不会改变;
    • 外部状态取决于具体的场景,并根据场景而变化,外部状态不能被共享;
  • 这样可以吧所有内部状态相同的对象都指定为同一个共享的对象;而外部状态从对象身上剥离并存储在外部;

文件上传例子

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 = '<span>文件名称:'+ this.fileName +', 文件大小: '
    + this.fileSize +'</span>' + '<button class="delFile">删除</button>';
  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 = '<span>文件名称:'+ fileName +', 文件大小: '
        + fileSize +'</span>' + '<button class="delFile">删除</button>';
      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) {
  ....
}
  • 在半成品方案情况下可以设置如此的责任链模式;
  • 耦合之间进行单元测试也更加简单;

职责链的优缺点

  • 优点
    • 解耦了请求发送者和多个接受者之间的关系;只需把请求传递给第一个节点;
    • 链中的节点对象可以灵活地拆分重组;
    • 可以手动设定起始节点;
  • 缺点
    • 由于不能确定每个节点都会正常处理;应该在链尾增加一个保底的接受者节点来处理即将离开链尾的请求;
    • 从性能考虑,应该避免过长的职责链带来的性能消耗;
原文地址:https://www.cnblogs.com/jinkspeng/p/4582456.html