JavaScript 设计模式(部分)

2019-01-28  本文已影响8人  咕嘟咕嘟li

1.单例模式
保证一个类仅有一个实例,并提供一个访问它的全局访问点。

var Singleton = function (name) {
  this.name = name;
  this.instance = null;
};
Singleton.prototype.getName = function () {
  console.log(this.name);
};
Singleton.getInstance = function (name) {
  if (!this.instance) {
    this.instance = new Singleton(name);
  }
  return this.instance;
};
var a = Singleton.getInstance( 'sven1' );
var b = Singleton.getInstance( 'sven2' );
console.log( a === b ); // true 
// 或者
var Singleton = function (name) {
  this.name = name;
};
Singleton.prototype.getName = function () {
  console.log(this.name);
};
Singleton.getInstance = (function () {
  var instance = null;
  return function (name) {
    if (!instance) {
      instance = new Singleton(name);
    }
    return instance;
  }
})();

2.策略模式
定义一系列的算法,把它们一个个封装起来,并且使它们可以相互替换。

例如代码实现:绩效为 S 的人年 终奖有 4 倍工资,绩效为 A 的人年终奖有 3 倍工资,而绩效为 B 的人年终奖是 2 倍工资。假设财 务部要求我们提供一段代码,来方便他们计算员工的年终奖

  1. 最初的代码实现
var calculateSalary = function (performance, salary) {
  var num = 1;
  switch (performance) {
    case 'S':
      num = 4;
      break;
    case 'A':
      num = 3;
      break;
    case 'B':
      num = 2;
      break;
  }
  return salary * num;
}
  1. 策略模式重构代码
// 1.封装绩效规则
var performanceS = function () { };
performanceS.prototype.calculate = function (salary) {
  return salary * 4;
};
var performanceA = function () { };
performanceA.prototype.calculate = function (salary) {
  return salary * 3;
};
var performanceB = function () { };
performanceB.prototype.calculate = function (salary) {
  return salary * 2;
};
// 2.定义奖金类 Bonus:
var Bonus = function () {
  this.salary = null; // 原始工资
  this.strategy = null; // 绩效等级对应的策略对象
};
Bonus.prototype.setSalary = function (salary) {
  this.salary = salary; // 设置员工的原始工资
};
Bonus.prototype.setStrategy = function (strategy) {
  this.strategy = strategy; // 设置员工绩效等级对应的策略对象
};
Bonus.prototype.getBonus = function () { // 取得奖金数额
  return this.strategy.calculate(this.salary); // 把计算奖金的操作委托给对应的策略对象
}

// 算法的使用
var bonus = new Bonus();
bonus.setSalary(10000);
bonus.setStrategy(new performanceS()); // 设置策略对象
console.log(bonus.getBonus()); // 输出:40000
bonus.setStrategy(new performanceA()); // 设置策略对象
console.log(bonus.getBonus()); // 输出:30000 

JavaScript 版本的策略模式:

var strategies = {
  'S': function (salary) {
    return salary * 4;
  },
  'A': function (salary) {
    return salary * 3;
  },
  'B': function (salary) {
    return salary * 2;
  }
};

var calculateBonus = function (level, salary) {
  return strategies[level](salary);
};
console.log(calculateBonus('S', 20000)); // 输出:80000
console.log(calculateBonus('A', 10000)); // 输出:30000 
  1. 发布—订阅模式
<!DOCTYPE html>
<html lang="en">
  <head>
    <title></title>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
  </head>
  <body>
    <input type="text" name="userName"/>
    <p></p>
    <script>
      var inp = document.querySelector('input');
      var p = document.querySelector('p');
      
      // 发布订阅对象
      var obj = {};
      // 发布订阅事件列表
      obj.list = {}
      // 定义订阅事件函数
      obj.listen = function (eventName, fn) {
        obj.list[eventName] = fn;
      }
      // 定义发布事件函数
      obj.trigger = function (eventName) {
        obj.list[eventName]();
      }
      
      // 数据
      var data = {str:'hello'}
      var str = data.str;
      // 观察数据变动从而触发所订阅事件
      Object.defineProperty(data, 'str', {
        get () {
          return str;
        },
        set (newVal) {
          str = newVal;
          console.log(data.str, data);
          obj.trigger('go')
        }
      })
      // 订阅一个事件
      obj.listen('go', function () {
        p.innerText = data.str;
        inp.value = data.str;
      })      
      // 数据变动
      inp.oninput = function () {
        data.str = this.value;
      }
    </script>
  </body>
</html>
  1. 模板方法模式
    模板方法模式是一种只需使用继承就可以实现的非常简单的模式.
    是基于继承的一种设计模式,父类封装了子类的算法框架和方法的执行顺序,
    子类继承父类之后,父类通知子类执行这些方法。
    例子:
var Beverage = function () { };
Beverage.prototype.boilWater = function () {
    console.log('把水煮沸');
};
Beverage.prototype.brew = function () {
    throw new Error('子类必须重写 brew 方法');
};
Beverage.prototype.pourInCup = function () {
    throw new Error('子类必须重写 pourInCup 方法');
};
Beverage.prototype.addCondiments = function () {
    throw new Error('子类必须重写 addCondiments 方法');
};
Beverage.prototype.customerWantsCondiments = function () {
    return true; // 默认需要调料
};
Beverage.prototype.init = function () {
    this.boilWater();
    this.brew();
    this.pourInCup();
    if (this.customerWantsCondiments()) { // 如果挂钩返回 true,则需要调料
        this.addCondiments();
    }
};
var CoffeeWithHook = function () { };
CoffeeWithHook.prototype = new Beverage();
CoffeeWithHook.prototype.brew = function () {
    console.log('用沸水冲泡咖啡');
};
CoffeeWithHook.prototype.pourInCup = function () {
    console.log('把咖啡倒进杯子');
};
CoffeeWithHook.prototype.addCondiments = function () {
    console.log('加糖和牛奶');
};
CoffeeWithHook.prototype.customerWantsCondiments = function () {
    return window.confirm('请问需要调料吗?');
};
var coffeeWithHook = new CoffeeWithHook();
coffeeWithHook.init();
  1. 职责链模式

假设我们负责一个售卖手机的电商网站,经过分别交纳 500 元定金和 200 元定金的两轮预定后(订单已在此时生成),现在已经到了正式购买的阶段。公司针对支付过定金的用户有一定的优惠政策。在正式购买后,已经支付过500 元定金的用户会收到 100 元的商城优惠券,200 元定金的用户可以收到 50 元的优惠券,而之前没有支付定金的用户只能进入普通购买模式,也就是没有优惠券,且在库存有限的情况下不一定保证能买到。

初步实现:

var order = function (orderType, pay, stock) {
    if (orderType === 1) { // 500 元定金购买模式
        if (pay === true) { // 已支付定金
            console.log('500 元定金预购, 得到 100 优惠券');
        } else { // 未支付定金,降级到普通购买模式
            if (stock > 0) { // 用于普通购买的手机还有库存
                console.log('普通购买, 无优惠券');
            } else {
                console.log('手机库存不足');
            }
        }
    } else if (orderType === 2) { // 200 元定金购买模式
        if (pay === true) {
            console.log('200 元定金预购, 得到 50 优惠券');
        } else {
            if (stock > 0) {
                console.log('普通购买, 无优惠券');
            } else {
                console.log('手机库存不足');
            }
        }
    } else if (orderType === 3) {
        if (stock > 0) {
            console.log('普通购买, 无优惠券');
        } else {
            console.log('手机库存不足');
        }
    }
};
order(1, true, 500);// 输出: 500 元定金预购, 得到 100 优惠券

职责链模式代码重构:

var order500 = function (orderType, pay, stock) {
    if (orderType === 1 && pay === true) {
        console.log('500 元定金预购,得到 100 优惠券');
    } else {
        return 'nextSuccessor'; // 我不知道下一个节点是谁,反正把请求往后面传递
    }
};
var order200 = function (orderType, pay, stock) {
    if (orderType === 2 && pay === true) {
        console.log('200 元定金预购,得到 50 优惠券');
    } 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.setNextSuccessor = 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;
};

// 把 3 个订单函数分别包装成职责链的节点:
var chainOrder500 = new Chain(order500);
var chainOrder200 = new Chain(order200);
var chainOrderNormal = new Chain(orderNormal);

// 指定节点在职责链中的顺序:
chainOrder500.setNextSuccessor(chainOrder200);
chainOrder200.setNextSuccessor(chainOrderNormal);

// 把请求传递给第一个节点:
chainOrder500.passRequest(1, true, 500); // 输出:500 元定金预购,得到 100 优惠券
chainOrder500.passRequest(2, true, 500); // 输出:200 元定金预购,得到 50 优惠券
chainOrder500.passRequest(3, true, 500); // 输出:普通购买,无优惠券
chainOrder500.passRequest(1, false, 0); // 输出:手机库存不足

优点:

缺点:

上一篇 下一篇

猜你喜欢

热点阅读