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

最近有时间看看书,看了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";
  }
}
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <style>
    #box{
      background: red;
      width: 100px;
      height: 200px;
    }
  </style>
</head>
<body>
  <div id="box"></div>
  <script src="./Animate.js"></script>
  <script>
    window.onload = function(){
      var animate  = new Animate("#box", "width", 200, 1000, "linear")
      animate.start()
    }
  </script>
</body>
</html>

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,772评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,458评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,610评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,640评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,657评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,590评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,962评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,631评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,870评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,611评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,704评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,386评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,969评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,944评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,179评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,742评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,440评论 2 342

推荐阅读更多精彩内容

  • javascript设计模式与开发实践 设计模式 每个设计模式我们需要从三点问题入手: 定义 作用 用法与实现 单...
    穿牛仔裤的蚊子阅读 4,032评论 0 13
  • 工厂模式 单体模式 模块模式 代理模式 职责链模式 命令模式 模板方法模式 策略模式 发布-订阅模式 中介者模式 ...
    HelloJames阅读 1,004评论 0 6
  • 【Android 动画】 动画分类补间动画(Tween动画)帧动画(Frame 动画)属性动画(Property ...
    Rtia阅读 6,095评论 1 38
  • 大爱的李老师,智慧的班主任,诸位亲爱的学兄们,大家好!我是青岛环宇印刷广告的谭京芳,今天(2019年1月5日)是我...
    2819336a9c2b阅读 133评论 0 0
  • 1 工作坊简介 1.1 背景 《接班人》新一期的社区营造项目点位于罗湖区笋岗街道洪湖社区。这是一个非常典型的老龄化...
    踏实玩乐阅读 200评论 0 0