# 其他设计模式

# 优先级划分依据

  • 不常用
  • 对应不到经典的应用场景

# 创建型

  • 原型模式

# 概念

  • clnoe 自己,生成一个新对象
  • java 默认有 clone 接口,不用自己实现

# JS 中的应用 -Object.create

// `Object.create` 用到了原型模式的思想(虽然不是 java 中的 clone)
// 基于一个原型创建一个对象
const prototype = {
  getName: function() {
    return this.first + '' + this.last
  },
  say: function() {
    console.log('hello')
  }
}

// 基于原型创建 x
const x = Object.create(prototype)
x.first = 'A'
x.last = 'B'
console.log(x.getName())
x.say()

// 基于原型创建 y
const y = Object.create(prototype)
y.first = 'C'
y.last = 'D'
console.log(y.getName())
y.say()

# 对比 JS 中的原型 prototype

  • prototype 可以理解为 ES6 class 的一种底层原理
  • 而 class 是实现面向对象的基础,并不是服务于某个模式
  • 若干年后 ES6 全面普及,大家可能会忽略掉 prototype
  • 但是 Object.create 却会长久存在

# 结构型

  • 桥接模式

# 概念

  • 用于把抽象化与实现化解耦
  • 使得二者可以独立变化

下面代码就是说,如果我们需要画一个红色的圆,跟黄色的三角形,我们需要抽象成一个颜色跟一个画的类, 将功能解耦,画的类就实现根据传递的参数绘制画出怎样的图形,而颜色类就是为画的图形填充。如果我们只是 简单的定义一个类,类里面有画红色圆形的方法,黄色三角形的方法等等,那么这种类在以后扩展起来也不太好。

class Color {
  constructor(name) {
    this.name = name
  }
}
class Shape {
  constructor(name, color) {
    this.name = name
    this.color = color
  }
  draw() {
    console.log(`${this.color.name} ${this.name}`)
  }
}

// 测试代码
let red = new Color('red')
let yellow = new Color('yellow')
let circle = new Shape('circle', red)
circle.draw()
let triangle = new Shape('trangle', yellow)
triangle.draw()

# 设计原则验证

  1. 抽象和实现分离,解耦
  2. 符合开放封闭原则
  • 组合模式

# 概念

  • 生成树形结构,表示 “整体-部分” 关系
  • 让整体和部分都具有一致的操作方式

# vnode 比较类似-虚拟dom

  <div id="div1" class="container">
    <p>123</p>
    <p>456</p>
  </div>

上面的 html 结构用虚拟 dom 来表示:

{
  tag: 'div',
  arrr: {
    id: 'div1',
    className: 'container'
  },
  children: [
    {
      tag: 'p',
      arrr: {},
      children: ['123']
    },
    {
      tag: 'p',
      arrr: {},
      children: ['456']
    }
  ]
}

# 演示

  • 整体和单个节点的操作是一致的
  • 整体和单个节点的数据结构也保持一致

# 设计原则验证

  1. 将整体和单个节点的操作抽象出来
  2. 符合开放封闭原则
  • 享元模式

# 概念

  • 共享内存(主要考虑内存,而非效率)
  • 相同的数据,共享使用

# 演示

下面例子是在一个 div 中有很多 a 标签绑定事件;如果给每一个进行绑定,如果 a 标签很多会消耗性能。

    let div1 = document.getElementById('div1')
    // 不可能每个a标签加一个点击事件,通过给父级添加一个代理实现
    div1.addEventListener('click', function (e) {
      let target = e.target
      // 判断如果点击的是a标签
      if (target.nodeName === 'A') {
        console.log(target.innerHTML)
      }
    })

# 设计原则验证

  • 将相同的部分抽象出来
  • 符合开放封闭原则

# 行为型

  • 策略模式

# 概念

  • 不同策略分开处理
  • 避免出现大量 if ... else 或者 switch ... case

# 演示

如果不使用策略模式:

class User {
  constructor(type) {
    this.type = type
  }
  buy() {
    if (this.type === 'ordinary') {
      console.log('普通用户购买')
    } else if (this.type === 'member') {
      console.log('会员用户购买')
    } else if (this.type === 'vip') {
      console.log('vip 用户购买')
    }
  }
}
// 测试代码
let v1 =  new User('ordinary')
v1.buy()
let v2 =  new User('member')
v2.buy()
let v3 =  new User('vip')
v3.buy()

使用策略模式:

class OrdinaryUser {
  buy() {
    console.log('普通用户购买')
  }
}

class MemberUser {
  buy() {
    console.log('会员用户购买')
  }
}

class VipUser {
  buy() {
    console.log('vip 用户购买')
  }
}

// 测试
let u1 = new OrdinaryUser()
u1.buy()
let u2 = new MemberUser()
u2.buy()
let u3 = new VipUser()
u3.buy()

# 设计原则验证

  1. 不同策略,分开处理,而不是混合在一起
  2. 符合开放封闭原则
  • 模板方法模式
class Action {
  handle() {
    this.handle1()
    this.handle2()
    this.handle3()
  }
  handle2() {

  }
  handle1() {

  }
  handle3() {

  }
}
  • 职责链模式

# 概念

  • 一步操作可能分为多个职责角色来完成
  • 把这些角色都分开,然后用一个链串起来
  • 将发起者和各个处理者进行隔离

# 演示


// 请假审批,需要组长审批、经理审批、最后总监审批
class Action {
  constructor(name) {
    this.name = name
    this.nextAction = null
  }
  setNextAction(action) {
    this.nextAction = action
  }
  handle() {
    console.log(`${this.name} 审批`)
    if(this.nextAction !== null) {
      this.nextAction.handle()
    }
  }
}

// test
let a1 = new Action('组长')
let a2 = new Action('经理')
let a3 = new Action('总监')
a1.setNextAction(a2)
a2.setNextAction(a3)
a1.handle()

# JS 中的链式操作

  • 职责链模式和业务结合较多,js 中能联想到链式操作
  • jQuery 的链式操作 Promise.then 的链式操作

# 设计原则验证

  1. 发起者与各个处理者进行隔离
  2. 符合开放封闭原则
  • 命令模式

# 示例


class Receiver {
  exec() {
    console.log('执行')
  }
}
class Command {
  constructor(receiver) {
    this.receiver = receiver
  }
  cmd() {
    console.log('触发命令')
    this.receiver.exec()
  }
}
class Invoker {
  constructor(command) {
    this.command = command
  }
  invoker() {
    console.log('开始')
    this.command.cmd()
  }
}

// test

// 士兵
let soldier = new Receiver()
// 小号手
let trumpeter = new Command(soldier)
// 将军
let general = new Invoker(trumpeter)
general.invoker()

# JS 中的应用

  1. 网页富文本编辑器操作,浏览器封装了一个命令对象
  2. document.execCommand('bold')
  3. document.execCommand('undo')
  • 备忘录模式

# 概念

  • 随时记录一个对象的状态变化
  • 随时可以恢复之前的某个状态(如撤销功能)

# 示例

// 状态备忘
class Memento {
  constructor(content) {
    this.content = content
  }
  getContent() {
    return this.content
  }
}

// 备忘列表
class CareTaker {
  constructor() {
    this.list = []
  }
  add(memento) {
    this.list.push(memento)
  }
  get(index) {
    return this.list[index]
  }
}

// 编辑器
class Editor {
  constructor() {
    this.content = null
  }
  setContent(content) {
    this.content = content
  }
  getContent() {
    return this.content
  }
  saveContentToMemento() {
    return new Memento(this.content)
  }
  getContentFromMemento(memento) {
    this.content = memento.getContent()
  }
}

// 测试代码
let editor = new Editor()
let careTaker = new CareTaker()

editor.setContent('111')
editor.setContent('222')
careTaker.add(editor.saveContentToMemento()) // 存储备忘录
editor.setContent('333')
careTaker.add(editor.saveContentToMemento()) // 存储备忘录
editor.setContent('444')

console.log(editor.getContent()) // 444
editor.getContentFromMemento(careTaker.get(1)) // 撤销
console.log(editor.getContent()) // 333
editor.getContentFromMemento(careTaker.get(0)) // 撤销
console.log(editor.getContent()) // 222
  • 中介者模式

如果每个对象之间互相调用接口,如果一个对象接口改变,那就所有的代码都会变,我们如果使用第二张图片那样,只需要改变中介着的那个接口就可以了。 在这里插入图片描述

# 示例 买房中介着

// 中介者
class Mediator {
  constructor(a, b) {
    this.a = a
    this.b = b
  }
  setA() {
    let number = this.b.number
    this.a.setNumber(number * 100)
  }
  setB() {
    let number = this.a.number
    this.b.setNumber(number / 100)
  }
}

class A {
  constructor() {
    this.number = 0
  }
  setNumber(num, m) {
    this.number = num
    if(m) {
      m.setB()
    }
  }
}
class B {
  constructor() {
    this.number = 0
  }
  setNumber(num, m) {
    this.number = num
    if(m) {
      m.setA()
    }
  }
}

// 测试

let a = new A()
let b = new B()
let m = new Mediator(a, b)
a.setNumber(100, m)
console.log(a.number, b.number)
b.setNumber(100, m)
console.log(a.number, b.number)

# 设计原则验证

  1. 将各个关联对象通过中介着隔离
  2. 符合开放封闭原则
  • 访问者模式

# 概念

  1. 将数据操作和数据结构进行分离
  • 解释器模式

# 概念

  1. 描述语言语法如何定义,如何解释和编译

评 论:

更新: 11/21/2020, 7:00:56 PM