乐趣区

笔记设计模式3提高复用性

提高可复用性

  • 遵循 DRY(Don’t repeat youself)原则
  • 减少代码量,节省开销
  • 对象重复使用
  • 模块功能单一

提高可复用性的模式

桥接模式

  • 通过独立方法间的桥接来组成整体,每个方法可以被高度复用
  • 目的:通过桥接代替耦合
  • 应用场景:减少模块之间的耦合
  • 基本结构

      // 例:有 3 种形状,每种形状都有 3 种颜色
      function rect(color) {showcolor(color)
      }
      function circle(color) {showcolor(color)
      }
      function delta(color) {showcolor(color)
      }
      // 公共的处理颜色的部分抽离出来,再桥接回去
      function showcolor(color) { }
      // 创建一个红色的圆形
      new circle('red')
  • 与建造者模式一样都是拆分再组合,不同在于

    • 建造者模式关注如何创建对象
    • 桥接模式关注于提高可复用性
  • 例 1:创建菜单按钮,并且给按钮绑定鼠标移进和移出事件,移进和移出显示颜色不同

    // 菜单类
      function menuItem(word, color) {
        this.word = word;
        this.color = color;
        this.dom = document.createElement('div');
        this.dom.innerHTML = this.word;
        document.getElementById('app').appendChild(this.dom);
      }
    
      menuItem.prototype.bind = function () {
        var self = this;
        this.dom.onmouseover = function () {console.log(self.color);
          this.style.color = self.color.colorOver;
        }
        this.dom.onmouseout = function () {this.style.color = self.color.colorOut;}
      }
      // 菜单颜色类
      function menuColor(colorover, colorout) {
        this.colorOver = colorover;
        this.colorOut = colorout;
      }
    
      var data = [{word: 'menu1', color: ['red', 'pink'] }, {word: 'menu2', color: ['green', 'pink'] }, {word: 'menu3', color: ['blue', 'pink'] }]
      for (var i = 0; i < data.length; i++) {new menuItem(data[i].word, new menuColor(data[i].color[0], data[i].color[1])).bind();}
    • 例 2:express 中的 get、post 等方法

享元模式

  • 提取公有和私有部分,私有部分作为外部数据传入,从而减少需要创建的对象数量
  • 目的:减少对象 / 代码的数量
  • 应用场景:当代码中创建了大量类似对象和类似的代码块
  • 基本结构

    • 只需一个类,类中保留共有的,每个弹窗不同的部分作为一个公共享元
      // 例:新建 100 个行为相同,仅样式不同的弹窗
      function Pop() {}
      // 行为相同
      Pop.prototype.action = function () {}
      // 样式不同
      Pop.prototype.show = function () {}
      // 提取出每个弹窗不同的部分作为外部数组
      var popArr = [{ text: 'window1', style: [400, 400] },
        {text: 'window2', style: [400, 200] }
      ]
      var poper = new Pop()
      for (var i = 0; i < 100; i++) {poper.show(popArr[i])
      }
  • 示例:jQuery 的 extend 方法

      $.extend({a: 1}) // 将对象添加到 jQuery 对象上
      $.extend({a: 1}, {b: 1}) // 返回合并后的对象 {a: 1, b: 1}
      // 模拟 extend 函数
      function extend () {
        // 判断参数个数,再循环,但是循环需要写两次
        /* if (arguments === 1) {for (var item in arguments[0]) {this[item] = arguments[0][item]
          }
        } else {for (var item in arguments[1]) {arguments[0][item] = arguments[1][item]
          }
        } */
        // 提取循环的不同部分,即复制的源和目的对象为公共享元
        var target = this
        var source = arguments[0]
        if (arguments === 2) {target = arguments[0]
          source = arguments[1]
        }
        for (var item in source) {target[item] = source[item]
        }
      }

模板方法模式

  • 当一个功能会朝着多样化发展时,先实现基础功能,其他多样化功能待之后通过扩展实现
  • 目的:定义一系列操作的骨架,避免类似操作
  • 应用场景:当项目中出现很多类似的操作内容
  • 基本结构

      // 例:导航组件,有的带消息提示,有的时竖的,有的是横的
      function baseNav() {// 基础类,定下基本骨架}
      baseNav.prototype.action = function (fn) {// 处理特异性,留出一个回调等待具体实现}
  • 例 1:基础弹窗和有特定功能的弹窗

    // 基础弹窗类
    function basePop(word, size) {
      this.word = word;
      this.size = size;
      this.dom = null;
    }
    // 初始化
    basePop.prototype.init = function () {var div = document.createElement('div');
      div.innerHTML = this.word;
      div.style.width = this.size.width + 'px';
      div.style.height = this.size.height + 'px';
      this.dom = div;
    }
    // 隐藏操作
    basePop.prototype.hide = function () {
      // 定义基础操作
      this.dom.style.display = 'none';
    }
    // 确认操作
    basePop.prototype.confirm = function () {
      // 定义基础操作
      this.dom.style.display = 'none';
    }
    // 点击按钮可以发送请求的弹窗类,继承自基础类
    function ajaxPop(word, size) {basePop.call(this, word, size);
    }
    ajaxPop.prototype = new basePop();
    // 保存父类的方法
    var hide = ajaxPop.prototype.hide;
    // 定义自己的 hide 方法
    ajaxPop.prototype.hide = function () {
      // 先调用父类的方法
      hide.call(this);
      // 再添加自己的实现
      console.log(1);
    }
    var confirm = ajaxPop.prototype.confirm;
    ajaxPop.prototype.confirm = function () {confirm.call(this);
      console.log(1);
    }
    var pop = new ajaxPop('sendmes', { width: 100, height: 300});
    pop.init();
    pop.confirm();
  • 例 2:算法计算器,支持在计算之前或者之后对数据进行处理

    // 基础的算法计算器,但是在不同使用场景中可能会在计算之前或者之后再添加一些计算操作
    function Counter() {this.beforeCount = []
     this.afterCount = []}
    // 计算之前添加处理
    Counter.prototype.addBefore = function (fn) {this.beforeCount = [fn]
    }
    // 计算之后添加处理
    Counter.prototype.addAfter = function (fn) {this.afterCount = [fn]
    }
    Counter.prototype.count = function (num) {
     var _resultnum = num // 放置最终结果
     var _arr = [] // 放置处理函数的队列
     _arr = this.beforeCount.concat(baseCount)
     _arr = _arr.concat(this.afterCount)
     function baseCount(num) {
       num += 4
       num *= 4
       return num
     }
     // 将数据依次放入队列中进行处理
     while (_arr.length > 0) {_resultnum = _arr.shift()(_resultnum)
     }
     return _resultnum
    }
    
    var c = new Counter()
    c.addBefore(function (num) {return num - 1})
    console.log(c.count(4))

组合与继承

组合

  1. JS 最初没有专门的继承,所以最初推崇函数式编程,然后统一组合桥接
  2. 桥接模式可以看成是组合的一种体现,组合的好处是耦合度低,方便复用,方便扩展

继承

  1. ES6 有了 class 和 extend,可以通过多种方式实现继承
  2. 模板方法模式可以看成是继承的一种体现,继承的好处是可以自动获得父类的内容和接口,方便统一化
退出移动版