JavaScript进阶之设计模式

1. 概念

1.1 设计模式

设计模式是解决某类问题的方案,由前人经过实践和经验总结出来的解决某类问题的思路。正确选择设计模式能够提高代码复用性和可维护性。

1.2 设计原则

  1. 单一职责原则
  2. 开放封闭原则
    可以通过增加代码的方式增加新功能,但是不能修改源码。

2. 工厂模式

工厂模式按照抽象程度不同可以分为:简单工厂模式、工厂方法模式、抽象工厂模式。

2.1 简单工厂模式

根据传入的参数不同,创建属性值不同的同类实例。
只能创建相同类型的实例

function simpleFactory(options) {
  var obj = new Object();
  obj.name = options.name;
  obj.roleId = options.roleId;
  obj.viewPages = options.viewPages || [];
  return obj;
}

// 测试
var user = simpleFactory({
  name: '管理员',
  roleId: 'Uu0183795',
  viewPages: ['首页', '菜单管理', '用户管理']
});
console.log('用户:', user);

2.2 工厂方法模式

根据传入类型不同,创建不同类型的实例。
通过子类来创建实例,可以创建不同类型的实例

function ComplexFactory() {}

ComplexFactory.prototype.make = function(type, options) {
  if (this instanceof ComplexFactory && this[type]) {
    var func = this[type];
    func.prototype = Object.create(ComplexFactory.prototype);
    return new func(options);
  }
  else {
    throw '不能创建类型为' + type + '的实例';
  }
}

ComplexFactory.prototype.extend = function(obj) {
  Object.keys(obj).forEach(key => {
    this[key] = obj[key];
  })
}

// 获取对象的viewPages
ComplexFactory.prototype.getViewPages = function() {
  return this.viewPages;
}

// 修改对象属性
ComplexFactory.prototype.setOptions = function(callback) {
  callback.call(this);
}

ComplexFactory.prototype.extend({
  superAdmin: function(options) {
    this.name = options.name;
    this.roleId = options.roleId;
    this.viewPages = options.viewPages || [];
  },
  admin: function(options) {
    this.name = options.name;
    this.roleId = options.roleId;
    this.viewPages = options.viewPages || [];
  },
  user: function(options) {
    this.name = options.name;
    this.roleId = options.roleId;
    this.viewPages = options.viewPages || [];
  },
})

// 测试
var factory = new ComplexFactory();
var admin = factory.make('admin', {
  name: '管理员',
  roleId: 'Uu0183795',
  viewPages: ['首页', '菜单管理', '用户管理']
});

console.log('admin:', admin);
console.log(admin.getViewPages());

admin.setOptions(function() {
  this.viewPages = [...this.viewPages, '权限管理'];
});

console.log('viewPages:', admin.viewPages);

var superAdmin = factory.make('superAdmin', {
  name: '超级管理员',
  roleId: 'U00000000',
  viewPages: ['首页', '菜单管理', '用户管理', '机构管理']
});

console.log('superAdmin', superAdmin);

2.3. 抽象工厂模式

抽象产品工厂返回的不是对象,而是能创建对象的工厂

function AbstractFactory(subClass, superType) {
  if (typeof AbstractFactory[superType] === 'function') {
    var superClass = AbstractFactory[superType];
    subClass.prototype = new superClass();
    subClass.prototype.constructor = subClass;
  }
}

AbstractFactory.superAdmin = function() {
  this.type = 'superAdmin';
}

AbstractFactory.admin = function() {
  this.type = 'admin';
}

AbstractFactory.user = function () {
  this.type = 'user';
}

function SuperAdmin(options) {
  this.name = options.name;
  this.roleId = options.roleId;
  this.viewPages = options.viewPages || [];
}

function Admin(options) {
  this.name = options.name;
  this.roleId = options.roleId;
  this.viewPages = options.viewPages || [];
}

function User(options) {
  this.name = options.name;
  this.roleId = options.roleId;
  this.viewPages = options.viewPages || [];
}

// 测试
AbstractFactory(SuperAdmin, 'superAdmin');

var superAdminAbs = new SuperAdmin({
  name: '超级管理员',
  roleId: 'U00000000',
  viewPages: ['首页', '菜单管理', '用户管理', '机构管理']
});

console.log('superAdminAbs:', superAdminAbs);

3. 单例设计模式

3.1 通过函数属性来实现

function User() {
  if (!(this instanceof User)) {
    throw '调用函数的方式不对,必须通过 new 关键字调用';
  }

  if (!User.instance) {
    User.instance = this;
  }

  return User.instance;
}

var user = new User();
var user1 = new User();
console.log('user === user1:', user === user1);

3.2 通过函数方法实现

function User() {
  this.name = '测试'
}

User.getInstance = function() {
  if (!User.instance) {
    User.instance = new User();
  }
  return User.instance;
}

var user1 = User.getInstance();
var user2 = User.getInstance();
console.log('user1 === user2:', user1 === user2);

3.3 立刻执行的函数表达式

var User = (function() {
  var instance;
  function _user() {
    this.name = 'name';
  }

  return function() {
    if (!instance) {
      instance = new _user();
    }
    return instance;
  }
})()

3.4 惰性单例模式

<!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>W3Cschool</title>
</head>
<body>
  <div id="loginBtn">登录</div>
</body>
<script>
  var getSingleton = function (fn) {
    var result;
    return function () {
      // 确定this上下文并传递参数
      return result || (result = fn.apply(this, arguments));
    }
  }
  // 该函数只会执行一次
  var createAlertMessage = function (html) {
    var div = document.createElement('div');
    div.innerHTML = html;
    div.style.display = 'none';
    document.body.appendChild(div);
    return div;
  }
  
  var createSingleAlertMessage = getSingleton(createAlertMessage);
  
  document.getElementById('loginBtn').onclick = function () {
    var alertMessage = createSingleAlertMessage('Test Content');
    alertMessage.style.display = 'block';
  }
</script>
</html>

4. 观察者模式

观察者模式中有两个角色,观察者(Observer)被观察者(Subject),观察者会订阅被观察者,被观察者会保存哪些观察者订阅了它,当被观察者状态改变时会触发观察者订阅被观察者时传入的回调函数。这就导致观察者和被观察者存在耦合关系。
在这里插入图片描述

//有一家猎人工会,其中每个猎人都具有发布任务(publish),订阅任务(subscribe)的功能
//他们都有一个订阅列表来记录谁订阅了自己
//定义一个猎人类
//包括姓名,级别,订阅列表
function Hunter(name, level){
	this.name = name
	this.level = level
	this.list = []
}
Hunter.prototype.publish = function (money){
	console.log(this.level + '猎人' + this.name + '寻求帮助')
    this.list.forEach(function(item, index){
    	item(money)
    })
}
Hunter.prototype.subscribe = function (targrt, fn){
	console.log(this.level + '猎人' + this.name + '订阅了' + targrt.name)
    targrt.list.push(fn)
}

//猎人工会走来了几个猎人
let hunterMing = new Hunter('小明', '黄金')
let hunterJin = new Hunter('小金', '白银')
let hunterZhang = new Hunter('小张', '黄金')
let hunterPeter = new Hunter('Peter', '青铜')

//Peter等级较低,可能需要帮助,所以小明,小金,小张都订阅了Peter
hunterMing.subscribe(hunterPeter, function(money){
	console.log('小明表示:' + (money > 200 ? '' : '暂时很忙,不能') + '给予帮助')
})
hunterJin.subscribe(hunterPeter, function(){
	console.log('小金表示:给予帮助')
})
hunterZhang.subscribe(hunterPeter, function(){
	console.log('小张表示:给予帮助')
})

//Peter遇到困难,赏金198寻求帮助
hunterPeter.publish(198)

5. 发布订阅模式

发布订阅模式中有三个角色 订阅者(Subscriber)发布者(publisher) 以及调度中心
发布者发布消息不会直接将消息发给订阅者,而是发给调度中心,调度中心再分发给订阅者。这就导致订阅者和发布者完全独立,互不影响。
在这里插入图片描述

//定义一家猎人工会
//主要功能包括任务发布大厅(topics),以及订阅任务(subscribe),发布任务(publish)
let HunterUnion = {
	type: 'hunt',
	topics: Object.create(null),
	subscribe: function (topic, fn){
	    if(!this.topics[topic]){
	      	this.topics[topic] = [];  
	    }
	    this.topics[topic].push(fn);
	},
	publish: function (topic, money){
	    if(!this.topics[topic])
	      	return;
	    for(let fn of this.topics[topic]){
	    	fn(money)
	    }
	}
}

//定义一个猎人类
//包括姓名,级别
function Hunter(name, level){
	this.name = name
	this.level = level
}
//猎人可在猎人工会发布订阅任务
Hunter.prototype.subscribe = function (topic, fn){
	console.log(this.level + '猎人' + this.name + '订阅了狩猎' + topic + '的任务')
    HunterUnion.subscribe(topic, fn)
}
Hunter.prototype.publish = function (topic, money){
	console.log(this.level + '猎人' + this.name + '发布了狩猎' + topic + '的任务')
    HunterUnion.publish(topic, money)
}

//猎人工会走来了几个猎人
let hunterMing = new Hunter('小明', '黄金')
let hunterJin = new Hunter('小金', '白银')
let hunterZhang = new Hunter('小张', '黄金')
let hunterPeter = new Hunter('Peter', '青铜')

//小明,小金,小张分别订阅了狩猎tiger的任务
hunterMing.subscribe('tiger', function(money){
	console.log('小明表示:' + (money > 200 ? '' : '不') + '接取任务')
})
hunterJin.subscribe('tiger', function(money){
	console.log('小金表示:接取任务')
})
hunterZhang.subscribe('tiger', function(money){
	console.log('小张表示:接取任务')
})
//Peter订阅了狩猎sheep的任务
hunterPeter.subscribe('sheep', function(money){
	console.log('Peter表示:接取任务')
})

//Peter发布了狩猎tiger的任务
hunterPeter.publish('tiger', 198)

6. 策略模式

就是将一系列的算法单独封装到一个策略对象中,使算法能够替换使用。
策略模式的目的就是将算法的使用和算法的实现进行分离。

6.1 面向对象方式

// 根据不同策略计算奖金
// 策略A
function PerformanceA() {}
PerformanceA.prototype.calculate = function(salary) {
  return salary * 4;
}
// 策略B
function PerformanceB() {}
PerformanceB.prototype.calculate = function(salary) {
  return salary * 2;
}
// 策略C
function PerformanceC() {}
PerformanceC.prototype.calculate = function(salary) {
  return salary * 1;
}

// 策略使用类
function Bonus() {
  // 工资
  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(1000);

bonus.setStrategy(new PerformanceA());
console.log('performanceA:', bonus.getBonus());  // 4000;

bonus.setStrategy(new PerformanceB());
console.log('performanceB:', bonus.getBonus());  // 2000

6.2 对象字面量方式

// 策略
var performance = {
  A: function(salary) {
    return salary * 4;
  },
  B: function(salary) {
    return salary * 2;
  },
  C: function(salary) {
    return salary;
  }
};

// 使用函数
var getBonus = function(level, salary) {
  return performance[level](salary);
}

console.log('A:', getBonus('A', 2000));  // 8000
console.log('B:', getBonus('B', 2000));  // 4000

7. 代理模式

为目标对象提供一个代理对象,来控制对目标对象的访问。

原则:
代理对象和目标对象的接口要具有一致性。以保证在不使用代理的情况下,可以直接使用目标对象。

7.1 保护代理

通过代理来处理掉一些无用的信息,就可以理解为保护代理。

举一个实际的例子(保护代理):
朋友来你家拜访,想要进入小区需要先去找门卫刷卡开门,门卫会先联系你,核实访客是否是你的朋友,然后你说是,门卫才会让访客进入。

// 目标对象
var You = function() {};
You.prototype.visit = function(identify) {
  console.log('identify:', identify);
  return { isAccepted: true };
}

// 代理对象,如果拜访人的名字是张三,不允许进入
// 否则,需要征求住户意见,以便确定是否允许访问者进入
var Guard = function() {}
Guard.prototype.visit = function (identify) {

  if (identify.name === '张三') {
    console.log(`不许${identify.name}进入`);
  }
  else {
    var res = new You().visit(identify);
    if (res.isAccepted) {
      console.log(`允许${identify.name}进入`);
    }
    else {
      console.log(`不许${identify.name}进入`);
    }
  }
}

// 测试
new Guard().visit({name: '李四'});
new Guard().visit({name: '张三'});

7.2 虚拟代理

代理对象在调用目标对象接口之前,做了某些其他的事情,就可以理解为虚拟代理。

例如: 通过代理实现图片预加载。

// 目标对象
var imageTarget = (function() {
  var imgEle = document.createElement('img');
  document.body.appendChild(imgEle);

  return {
    setSrc: function(src) {
      imgEle.src = src;
    }
  }
})();

// 代理对象
var proxy = (function() {
  var image = new Image();

  image.onload = function() {
    var _this = this;
    setTimeout(function() {
      imageTarget.setSrc(_this.src);
    }, 500);
  }

  return {
    setSrc: function(src) {
      // 预加载静态图片
      imageTarget.setSrc('./one.png');
      image.src = src;
    }
  }
})();

// 测试
proxy.setSrc('http://...')

7.3 缓存代理

7.3.1 静态缓存代理

// 计算所有参数的乘积
var mult = function() {
  var result = 1;
  for (var i = 0; i < arguments.length; i++) {
    result *= arguments[i];
  }
  return result;
}

// 代理对象
var multProxy = (function() {
  var cache = {};

  return {
    mult: function() {
      var key = Array.prototype.join.call(arguments, ',');
      if (key in cache) {
        return cache[key];
      }
      return cache[key] = mult.apply(this, arguments);
    }
  }
})();

console.log(multProxy.mult(1, 2, 4, 5, 6)); // 240

7.3.2 动态缓存代理

通过缓存工厂动态生成某个代理对象。

// 计算所有参数的乘积
var mult = function() {
  var result = 1;
  for (var i = 0; i < arguments.length; i++) {
    result *= arguments[i];
  }
  return result;
}

// 计算所有参数的和
var plus = function() {
  var result = 1;
  for (var i = 0; i < arguments.length; i++) {
    result += arguments[i];
  }

  return result;
}

// 代理对象工厂
var proxyFactory = function(fn) {
  var cache = {};

  return function() {
    var key = Array.prototype.join.call(arguments, ',');
    if (key in cache) {
      return cache[key];
    }
    return cache[key] = fn.apply(this, arguments);
  }
}

// 测试
var multPro = proxyFactory(mult);
var plusPro = proxyFactory(plus);

console.log(multPro(1, 2, 4, 5, 6)); // 240
console.log(plusPro(1, 2, 4, 5, 6)); // 19

8. 命令模式

使用一种松耦合的方式来设计程序,使命令触发者命令接受者能够没有耦合关系。

命令可以理解为一种事件名。
命令触发者通过触发某个命令来调用命令接受者上绑定的命令对应的函数。

// 命令接受对象
var Editor = function() {
  // 当前值
  this.content = '';
  // 历史记录
  this.history = [];
  // 栈中的指针
  this.historyPosition = -1;
}

Editor.prototype = {
  constructor: Editor,

  // 编辑命令
  edit: function(content) {
    this.content = this.content + content + '';
    if (this.historyPosition < this.history.length - 1) {
      this.history = this.history.slice(0, this.historyPosition + 1);
    }
    this.history.push(this.content);
    this.historyPosition++;
    console.log('edit content:', this.content);
  },

  // 撤回
  undo: function() {
    if (this.canUndo()) {
      this.historyPosition--;
      this.content = this.history[this.historyPosition];
      console.log('undo content:', this.content);
    }
  },

  // 重做
  redo: function() {
    if (this.canRedo()) {
      this.historyPosition++;
      this.content = this.history[this.historyPosition];
      console.log('redo content:', this.content);
    }
  },

  // 是否可以撤回
  canUndo: function() {
    return this.history.length > 1 && this.historyPosition > 0;
  },

  // 是否可以重做
  canRedo: function() {
    return this.history.length > 1 && this.historyPosition < this.history.length - 1;
  }
}

// 命令触发对象
var CommandTrigger = function(editor) {
  this.editor = editor;
  this.edit = function(content) {
    this.editor.edit(content);
  };
  this.undo = function() {
    this.editor.undo();
  }
  this.redo = function() {
    this.editor.redo();
  }
}

var commandTrigger = new CommandTrigger(new Editor());
commandTrigger.edit('Hello ');
commandTrigger.edit('World!');
commandTrigger.undo();
commandTrigger.edit('World!!!');
commandTrigger.undo();
commandTrigger.redo();

9. 组合模式

9.1 应用场景:

  1. 存在有层次结构的对象。
  2. 要操纵这批对象或者其中的部分对象。

9.2 特点

  1. 组合模式中有两种对象:
    组合对象,由叶子对象或者聚合了叶子对象的组合对象聚合而成。
    叶子对象,最底层的目标对象。
  2. 通过调用组合对象的方法,隐式调用叶子对象的方法来实现某个功能。
  3. 组合对象和叶子对象要实现同一批接口。
    在这里插入图片描述

9.3 实例

在这里插入图片描述

var Composite = function(name) {
  this.name = name;
  this.type = 'composite';
  this.children = [];
}

Composite.prototype = {
  constructor: Composite,
  // 添加叶子对象
  addChild: function(child) {
    this.children.push(child);
    return this;
  },
  getChild: function (name) {
    if (name === undefined) throw new Error('getChild 方法需要传递参数。');

    var elements = [];

    var getEle = function(item) {
      if (item.type === 'leaf') {
        item.name === name && elements.push(item);
      }
      else {
        item.children.forEach(getEle);
      }
    }

    this.children.forEach(getEle);

    return elements;
  }
}

var Leaf = function(name) {
  this.name = name;
  this.type = 'leaf';
}

Leaf.prototype = {
  constructor: Leaf,
  getChild: function(name) {},
  addChild: function (name) {
    return false;
  }
}
// 测试
var composite = new Composite('公司');
composite
  .addChild(
    new Composite('北京分公司')
      .addChild(new Leaf('赵五'))
      .addChild(new Leaf('秦六'))
  )
  .addChild(
    new Composite('长沙分公司')
      .addChild(new Leaf('张三'))
      .addChild(new Leaf('李四'))
  )
  .addChild(new Leaf('张三'))

console.log(composite);
console.log('getChild:', composite.getChild('张三'))

10. 模板方法模式

父类中封装子类的算法架构和公共方法,子类继承并重写父类的方法。

// 父类
var Interview = function() {};

Interview.prototype = {
  constructor: Interview,
  // 算法架构
  init: function(){
    console.log('开始面试');
    this.writtenTest();
    this.technicalInterview();
    this.leader();
    this.waitNotice();
  },
  // 笔试
  writtenTest: function() {
    console.log('笔试');
  },
  // 技术面试
  technicalInterview: function() {
    console.log('技术面试');
  },
  // 领导或者HR面试
  leader: function() {
    console.log('领导面试');
  },
  // 等通知
  waitNotice: function() {
    console.log('等通知');
  }
}

// 百度面试流程
var BaiDuInterview = function() {}
BaiDuInterview.prototype = Object.create(Interview.prototype);
BaiDuInterview.prototype.writtenTest = function() {
  console.log('百度——笔试');
}
BaiDuInterview.prototype.technicalInterview = function() {
  console.log('百度——技术面试');
}
BaiDuInterview.prototype.leader = function () {
  console.log('百度——领导面试');
}
BaiDuInterview.prototype.waitNotice = function() {
  console.log('百度——等通知');
}

// 测试
var baiDuInterview = new BaiDuInterview();
baiDuInterview.init();

11. 享元模式

是一种用于优化的设计模式,主要目标是为了减少相似对象的数量。
即,通过修改有一类对象的属性值来实现功能,而不是重复创建相似的对象来解决问题。

11.1 实例

学生体检,男生和女生的体检项目不同,但是男生或者女生的体检项目是相同,就可以根据性别创建两类对象,然后修改对象的属性来进行体检。不需要给每个学生都创建一个对象来进行体检。

var Fitness = function(sex) {
  this.sex = sex;
}

// 工厂模式-根据性别不同创建不同的对象
var FitnessFactory = {
  objs: {},
  create: function(sex) {

    if (!this.objs[sex]) {
      this.objs[sex] = new Fitness(sex);
    }

    return this.objs[sex];
  }
}

// 管理器,显示数据添加,以及对象数据更新
var FitnessManager = {
  fitnessData: {},
  add: function(name, sex, age, height, weight) {
    var fitness = FitnessFactory.create(sex);

    this.fitnessData[name] = {
      name: name,
      age: age,
      height: height,
      weight: weight
    }

    return fitness;
  },

  updateFitnessData: function(name, obj) {
    var fitnessData = this.fitnessData[name];

    for (var item in fitnessData) {
      if (fitnessData.hasOwnProperty(item)) {
        obj[item] = fitnessData[item];
      }
    }
  }
}

Fitness.prototype.judge = function(name) {
  FitnessManager.updateFitnessData(name, this);

  var result = name + ':';
  if (this.sex === 'male') {
    result += this.maleJudge();
  }
  else {
    result += this.femaleJudge();
  }

  console.log(result);
  return result;
}

Fitness.prototype.maleJudge = function() {
  return '男性测试结果';
}

Fitness.prototype.femaleJudge = function() {
  return '女性测试结果';
}

// 测试
var a = FitnessManager.add('张三', 'male', 20, 180, 180);
var b = FitnessManager.add('李四', 'male', 20, 180, 180);
var c = FitnessManager.add('王五', 'female', 20, 180, 180);

a.judge('张三')
console.log('a:', a);
b.judge('李四')
console.log('b:', b);
c.judge('王五')
console.log('c:', c);

12. 职能链模式

就是处理请求的对象作为节点的链表数据结构,从链表的第一个节点开始处理请求,如果该节点可以用来处理该请求,则使用该节点处理,如果不能处理,则使用下一个节点进行处理,依次类推,直到遍历完整个链表。

12.1 实例

var ChainItem = function(fn) {
  this.fn = fn;
  this.next = null;
}

ChainItem.prototype = {
  constructor: ChainItem,
  // 设置下一个节点
  setNext: function(nextChain) {
    this.next = nextChain;
    return this.next;
  },
  // 处理请求
  start: function() {
    this.fn.apply(this, arguments);
  },
  toNext: function() {
    if (this.next) {
      this.next.start.apply(this.next, arguments);
    }
    else {
      return {flag: false, msg: '请求未处理'}
    }
  }
}

var operateNumber = function(num) {
  if (typeof num === 'number') {
    console.log('operateNumber', num);
  }
  else {
    this.toNext(num);
  }
}

var operateString = function(str) {
  if (typeof str === 'string') {
    console.log('operateString', str);
  }
  else {
    this.toNext(str);
  }
}

var chainItemNumber = new ChainItem(operateNumber);
var chainItemString = new ChainItem(operateString);
chainItemNumber.setNext(chainItemString);
chainItemNumber.start('90');

13. 中介者模式

很多对象通过中介者对象来实现某个功能,实现对象之间的解耦。例如,React使用的Redux、Vue使用的Vuex。
在这里插入图片描述

13.1 实例

// 中介者,计算分数排名
var rankScore = function(person) {
  return [A.score, B.score].sort(function(a, b) {
    return b - a;
  });
}

var A = {
  score: 0,
  changeTo: function(score) {
    this.score = score;
    return rankScore(A);
  }
}

var B = {
  score: 0,
  changeTo: function(score) {
    this.score = score;
    return rankScore(B);
  }
}

console.log(A.changeTo(100));
console.log(B.changeTo(200));

14. 装饰者模式

再不改变对象原有功能的基础上,在程序运行过程中给对象添加新的功能。

14.1 实例

// 原对象
function Person() {}

Person.prototype.skill = function() {
  console.log('数学');
}

// 装饰者1
function MusicDecorator(person) {
  this.person = person;
}

MusicDecorator.prototype.skill = function() {
  this.person.skill();
  console.log('音乐');
}

// 装饰者2
function SportDecorator(person) {
  this.person = person;
}

SportDecorator.prototype.skill = function() {
  this.person.skill();
  console.log('运动');
}

// 测试
var person = new Person();
var musicDecorator = new MusicDecorator(person);
console.log('musicDecorator:', musicDecorator.skill());
var sportDecorator = new SportDecorator(musicDecorator);
console.log('sportDecorator:', sportDecorator.skill());

15. 状态模式

通过对象内部状态的改变来控制对象的行为。
通常吧每种内部状态封装成一个类,影响对象的行为也封装在内部状态类中。

15.1 实例

以一个人的工作状态作为例子,在刚醒、精神、疲倦几个状态中切换着。

function Work(name) {
  this.name = name;
  this.currentState = null;

  // 状态
  this.wakeUpState = new WakeUpState(this);
  this.energeticState = new EnergeticState(this);
  this.tiredState = new TiredState(this);

  this.init();
}

Work.prototype.init = function() {
  this.currentState = this.wakeUpState;
  var _this = this;
  document.body.onclick = function() {
    _this.currentState.behavior();
  }
}

Work.prototype.setState = function(state) {
  this.currentState = state;
  state.behavior();
}

function WakeUpState(work) {
  this.work = work;
}
// 状态1
WakeUpState.prototype.behavior = function() {
  console.log(this.work.name + ': 刚刚睡醒。');

  setTimeout(function() {
    this.work.setState(this.work.energeticState);
  }.bind(this), 1000);
}
// 状态2
function EnergeticState(work) {
  this.work = work;
}

EnergeticState.prototype.behavior = function() {
  console.log(this.work.name + ': 尽力充沛。');

  setTimeout(function() {
    this.work.setState(this.work.tiredState);
  }.bind(this), 1000);
}
// 状态3
function TiredState(work) {
  this.work = work;
}

TiredState.prototype.behavior = function() {
  console.log(this.work.name + ': 太累了。');
}
// 测试
new Work('gaoli');

16. 适配器模式

两个软件实体之间的接口数据格式不匹配,可以通过适配器做格式转换。

16.1 实例

// 渲染数据,格式限制为数组了
function renderData(data) {
    data.forEach(function(item) {
        console.log(item);
    });
}

// 对非数组的进行转换适配
function arrayAdapter(data) {
    if (typeof data !== 'object') {
        return [];
    }

    if (Object.prototype.toString.call(data) === '[object Array]') {
        return data;
    }

    var temp = [];

    for (var item in data) {
        if (data.hasOwnProperty(item)) {
            temp.push(data[item]);
        }
    }

    return temp;
}

var data = {
    0: 'A',
    1: 'B',
    2: 'C'
};

renderData(arrayAdapter(data)); // A B C

17. 外观模式

为一组接口提供一个高级接口,通过这个高级接口来调用这组接口。

// 三个处理函数
function start() {
    console.log('start');
}

function doing() {
    console.log('doing');
}

function end() {
    console.log('end');
}

// 外观函数,将一些处理统一起来,方便调用
function execute() {
    start();
    doing();
    end();
}


// 调用init开始执行
function init() {
    // 此处直接调用了高层函数,也可以选择越过它直接调用相关的函数
    execute();
}

init(); // start doing end

https://www.cnblogs.com/imwtr/p/9451129.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值