zoukankan      html  css  js  c++  java
  • 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 authorization basic
    第二十一章 单例模式
    第十九章 组合模式
    第十八章 备忘录模式
    第十七章 适配器模式
    第十六章 状态模式
    新博客~
    CF922D Robot Vacuum Cleaner
    BZOJ1767 [CEOI2009]harbingers
    树的直径学习笔记
  • 原文地址:https://www.cnblogs.com/jinkspeng/p/4582456.html
Copyright © 2011-2022 走看看