设计模式(一)单例模式和策略模式

最近有时间看看书,看了JavaScript设计模式与开发实践这本书,之前草草看过这本书,已经忘的差不多了,很多关于设计模式的书都是基于类的,这本书基于javascript的语言特点讲了设计模式,把看到的实践总结一遍才会变成自己的。

单例模式

这应该是最简单的一种设计模式,在应用中我们通常需要一个单独的实例来实现一些功能,比如线程池,登录框,任务队列。
例如这样

function Single(name) {
  this.name = name;
  this.instance = null;
}
Single.getInstance = function(name) {
  if (!this.instance) {
    this.instance = new Single(name);
  }
  return this.instance;
};
var a = Single.getInstance("小明");
var b = Single.getInstance("小周");
console.log(a === b);

我们实现一个单例的方法还需要创建一个类,在执行一个类上的方法,这样写代码有些受java等面向对象语言的干扰,利用js的特性可以直接实现一个单例模式,全局变量就是唯一的一个实例,但使用全局变量会引起变量污染问题,我们可以使用闭包来实现一个简单的单例

// 实现一个代理的通用方法
var single = (function() {
  var instance;
  return function getInstance(fn) {
    if (!instance) {
      instance = fn.apply(this, [].slice.call(arguments, 1));
    }
    return instance;
  };
})();

// 单例模式具体执行的方法
var createDiv = function(html) {
  var div = document.createElement("div");
  div.innerHTML = html;
  document.body.appendChild(div);
  return div;
};

var div1 = single(createDiv, "single1");
var div2 = single(createDiv, "single2");
console.log(div1 === div2);

根据单一职责的实践,我们将负责单一实例的功能与具体执行的方法分开设计,这样保证了更好的通用性,和方便以后对方法进行修改。其实我们在一些常用的方法中已经用过这种模式,比如我们通常用来优化页面滚动的函数截流函数防抖。都是在滚动的时候保证只有一个单例的方法去执行自定义的逻辑,防止函数被多次调用。

策略模式

我们在写代码时经常会写if else逻辑判断的代码
例如年终评绩效时得 A 会发 4 倍工资,B 是 3 倍工资,C 是 2 倍工资

/**
 * 获取年终奖金
 * @param {string} performance // 绩效
 * @param {number} wage 基本工资
 */
function getBouns(performance, wage) {
  if (performance === "A") {
    return 4 * wage;
  } else if (performance === "B") {
    return 4 * wage;
  } else if (performance === "C") {
    return 4 * wage;
  }
}

这是很常见的代码编写,当逻辑比较少的时候这样写是最佳的,但当我们有很多判断逻辑,并且之后可能会频繁修改就需要用到策略模式了。下边是用传统方式实现的策略模式

// 下边是绩效类
function performanceA() {}
performanceA.prototype.caculate = function(wage) {
  return 4 * wage;
};
function performanceB() {}
performanceB.prototype.caculate = function(wage) {
  return 3 * wage;
};
function performanceC() {}
performanceC.prototype.caculate = function(wage) {
  return 2 * wage;
};
/**
 * 工资计算类
 * @param {*} performance
 * @param {*} wage
 */
function Bouns(performance, wage) {
  this.performance = performance;
  this.wage = wage;
}
Bouns.prototype.getBouns = function() {
  return this.performance.caculate(this.wage);
};

var bouns = new Bouns(new performanceA(), 10000);
console.log(bouns.getBouns());

在 java中我们要实现任何方法都要依赖类来实现,通过类去实例化对象。我们想要针对不同的条件去执行不同的类,其实还是要ifelse的判断。二期对于一个固定的对象我们还实现了构造函数,有些画蛇添足。在js中我们可以直接定义对象字面量,向下边这样

// 定义所有策略对象
var performances = {
  A: function(wage) {
    return wage * 4;
  },
  B: function(wage) {
    return wage * 3;
  },
  C: function(wage) {
    return wage * 2;
  }
};

function getBouns(performance, wage) {
  if (performances[performance]) {
    return performances[performance](wage);
  }
  throw new Error("performance is not define");
}

var bouns = getBouns("A", 10000);
console.log(bouns);

这里直接使用一个对象来接收所有计算奖金的逻辑,我们的业务代码getBouns在遇到修改比如我想加一个S的绩效不用改变,我们直接修改performances这个承载所有计算逻辑的对象就可以实现业务的修改

这里再举一个例子,我们可以使用策略模式实现一个js动画,我们使用动画的时候经常会用css的动画,transtion的属性有ease,linear代表运动速率函数,left,width代表要改变的css属性,我们可以使用策略模式来简单实现一个基于js的动画方法

// 计算运动的算法,4个参数是已消耗时间,原始位置,位置间的差距,持续的总时间
const motions = {
  linear: function(t, b, c, d) {
    return (c * t) / d + b;
  },
  ease: function(t, b, c, d) {
    return c * (t /= d) * t + b;
  }
};

/**
 * 动画的主方法,模仿css的属性参数
 */
class Animate {
  /**
   *
   * @param {*} dom 需要修改的dom元素
   * @param {*} property 要改变的style属性
   * @param {*} target 改变style的目标值
   * @param {*} time 运动的时间
   * @param {*} motion 运动函数
   */
  constructor(dom, property, target, time, motion) {
    this.dom = document.querySelector(dom);
    this.property = property;
    this.startDom = +getComputedStyle(this.dom)[property].replace("px", "");
    this.startTime = +new Date();
    this.curTime = this.startTime;
    this.time = time;
    this.end = this.startTime + time;
    this.endDom = target;
    this.motion = motions[motion];
  }
  /**
   * 开始执行动画
   */
  start() {
    this.timer = setInterval(() => {
      this.step();
    }, 60);
  }
  // 每一步执行的具体步骤
  step() {
    this.curTime = +new Date();
    if (this.curTime > this.end) {
      clearInterval(this.timer);
      this.update(this.endDom);
      return;
    }
    var p = this.motion(
      this.curTime - this.startTime,
      this.startDom,
      this.endDom - this.startDom,
      this.time
    );
    this.update(p);
  }
  // 更新dom的方法
  update(value) {
    this.dom.style[this.property] = value + "px";
  }
}



  
  
  
  Document
  


  

这样利用策略模式我们就可以模拟一个类似css动画的方法库,在页面中可以看到一个方块匀速变大到200px的过程。我们想换一种运动方式只需要将linear改为ease。还有很多种实际应用,我们可以在实际开发中应用,例如表单校验,目前组件化流行,根据不同的数据展示不同的组件。都可以帮我们更好的优化代码。不用设计也可以实现所有功能,设计模式只是帮我们更好的使代码逻辑更清晰,更好维护代码,更好复用代码。

你可能感兴趣的:(设计模式(一)单例模式和策略模式)