Skip to content

设计模式

一、创建型

1、工厂模式

介绍

  • new操作单独封装
  • 构造函数和创建者分离
  • 减少每次new操作
  • 符合开放封闭原则

示列

jQuery $操作符
通过工厂模式返回一个实例

查看代码
javascript
// 构造函数和创建者分离
function Jquery (selector) {
  this.selector = document.querySelectorAll(selector)
}
// new操作单独封装
window.$ = function (selector) {
  return new Jquery(selector)
}
var p = $('div')
console.log(p)

2、单例模式

介绍

  • 系统中被唯一使用
  • 一个类中只有一个实例
  • 符合单一性原则

示列

查看代码
javascript
class SingleObject {
    showMessage() {
      console.log('showMessage', this)
    }
}
// 使用自执行函数,创建一个实例
SingleObject.getInstance = (function () {
    let instance = null
    return function () {
      if (!instance) {
        instance =  new SingleObject()
      }
      return instance
    }
})()

console.log(SingleObject.getInstance() === SingleObject.getInstance())

3、原型模式

clone自己,生成一个新对象

Object.create

二、结构型

1、适配器模式

介绍

  • 封装旧接口
  • 符合开放封闭原则

示列

查看代码
javascript
class SpecialRequest {
    request() {
      return 'old'
    }
}

class Request {
  constructor() {
    // 封装旧接口
    this.adptor = new SpecialRequest()
  }
  request () {
    let type = this.adptor.request()
    console.log(type)
  }
}
new Request().request()

vue中computed

2、装饰器模式

介绍

  • 为对象添加新功能
  • 将现有对象和装饰器进行分离
  • 不改变其原有的结构功能
  • 符合开放封闭原则

示列

查看代码
javascript
class Circle {
    constructor() {
        this.border = 'none'
    }
    draw() {
        console.log('draw Circle', this)
    }
}

class Client {
    constructor(circle) {
        this.circle = circle
    }
    draw() {
        // 添加新功能
        this.circle.draw()
        this.setBorder(this.circle)
        this.circle.draw()
    }
    setBorder(circle) {
        circle.border = '1px'
        console.log('setBorder', circle)
    }
}
new Client(new Circle()).draw()

3、代理模式

介绍

  • 使用者无权访问目标对象
  • 中间加代理,通过代理做授权和控制
  • 代理类和目标类分离
  • 符合开放封闭原则

示列

查看代码
javascript
class ReadImg {
  constructor(fileName) {
    this.fileName = fileName
    this.loadImg()
  }
  show() {
    console.log('show image', this.fileName)
  }
  loadImg() {
    console.log('load image', this.fileName)

  }
}
class ProxyImg {
  constructor(fileName) {
    this.img = new ReadImg(fileName)
  }
  show() {
    this.img.show()
  }
}

new ProxyImg('www.png').show()

场景

  • 网页事件代理
  • jQuery中$.proxy
  • es6 proxy

VS适配器模式

适配器模式:提供一个不同的接口
代理模式:提供一模一样的接口

VS装饰者模式

装饰器模式:扩展功能,原有功能不变且可直接使用
代理模式:针对原有功能,但是有限制或阉割

4、外观模式

  • 为子系统的一组接口提供高层接口
  • 使用者使用这个高层接口
  • 不符合单一原则和开放封闭原则

示列

查看代码
javascript
function bind(ele, type, selector, fn) {
  if (fn == null) {
    fn = selector
    selector = null
  }
  // ***
}
// 支持两种方式调用
bind('.app', 'click', '.btn', fn)
bind('.app', 'click', fn)

5、桥接模式

  • 将抽象和实现分离,使两者可以独立变化

6、组合模式

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

7、享元模式

介绍

  • 共享内存(主要考虑内存,而非效率)
  • 相同的数据,共享使用
  • 相同的JS可以共享,不同的JS不能共享
  • 将相同的部分抽象出来

三、行为型

1、观察者模式

介绍

  • 发布&订阅
  • 一对多(1或n)
查看代码
javascript
// 主题,接收状态变化,触发每个观察者
class Subject {
    constructor() {
        this.state = 0
        this.observers = []
    }
    getState() {
        return this.state
    }
    setState(state) {
        this.state = state
        this.notifyAllObservers()
    }
    attach(observer) {
        this.observers.push(observer)
    }
    notifyAllObservers() {
        this.observers.forEach(observer => {
            observer.update()
        })
    }
}

// 观察者,等待被触发
class Observer {
    constructor(name, subject) {
        this.name = name
        this.subject = subject
        this.subject.attach(this)
    }
    update() {
        console.log(`${this.name} update, state: ${this.subject.getState()}`)
    }
}

// 测试代码
let s = new Subject()
let o1 = new Observer('o1', s)
let o2 = new Observer('o2', s)
let o3 = new Observer('o3', s)

s.setState(1)
s.setState(2)
s.setState(3)

应用

  • 网页事件绑定
  • Promise
  • jQuery callbacks
  • nodejs自定义事件
查看代码
javascript
const EventEmitter = require('events').EventEmitter
const emitter1 = new EventEmitter()
emitter1.on('some', info => {
    console.log('fn1', info)
})
emitter1.on('some', info => {
    console.log('fn2', info)
})
emitter1.emit('some', 'xxxx')
  • vue和react组件生命周期触发
  • vue watch

2、迭代器模式

顺序访问一个集合 使用者无需知道集合的内部结构

查看代码
js

// 迭代器模式
class Iterator {
    constructor(container) {
        this.list = container.list
        this.index = 0
    }
    hasNext() {
        return this.index < this.list.length
    }
    next() {
        if (this.hasNext()) {
            return this.list[this.index++]
        }
        return null
    }
}

class Container {
    constructor(list) {
        this.list = list
    }
    getGetIterator() {
        return new Iterator(this)
    }
}

const arr = [100, 99, 3]
const list = new Container(arr)
const generate = list.getGetIterator()
console.log(generate.next())
console.log(generate.next())
console.log(generate.next())
console.log(generate.next())

使用场景

  • jQuery each

  • ES6 Iterator

有序数据集合都有 [Symbol.iterator] 属性

属性值是函数,执行函数返回一个迭代器,迭代器有next方法顺序返回子元素

可运行 Array.prototype [Symbol.iterator] 来测试

查看代码
js
const arr = [100, 99, 3]

// ES6 iterator
const iterator = arr[Symbol.iterator]()
// 有数据返回 {value: 100, done: false}
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())
// 没有数据返回 {value: undefined, done: true}
console.log(iterator.next())

// for of 语法
function each(data) {
    // iterator 实现
    const iterator = data[Symbol.iterator]()
    let item = { done: false }
    while (!item.done) {
        item = iterator.next()
        if (!item.done) {
            console.log(item.value)
        }
    }
    // for of 实现【带有遍历器特性的对象 Symbol.iterator有值】
    for (const item of data) {
        console.log(item)
    }
}

each(arr)

3、状态模式

  • 一个对象有状态变化
  • 每次状态变化都会触发一个逻辑
  • 不能总是用if...else来控制

示例

查看代码
js

// 状态模式
class State {
    constructor(color) {
        this.color = color
    }
    handle(context) {
        console.log(`turn to ${this.color} light`)
        context.setState(this)
    }
}

class Context {
    constructor() {
        this.state = null
    }
    getState() {
        return this.state
    }
    setState(state) {
        this.state = state
    }
}

// 测试代码
let context = new Context()

let green = new State('green')
let yellow = new State('yellow')
let red = new State('red')

green.handle(context)
yellow.handle(context)
red.handle(context)

场景

  • 有限状态机
查看代码
js

import StateMachine from 'javascript-state-machine'
import $ from 'jquery'

// 初始化状态模型

let fsm = new StateMachine({
    init: '收藏',
    transitions: [
        {
            name: 'doStore',
            from: '收藏',
            to: '取消收藏'
        },
        {
            name: 'deleteStore',
            from: '取消收藏',
            to: '收藏'
        }
    ],
    methods: {
        // 监听执行收藏
        onDoStore: function() {
            alert('收藏成功')
            updateText()
        },
        // 监听取消收藏
        onDeleteStore: function() {
            alert('已经取消收藏')
            updateText()
        }
    }
})

let $btn = $('#btn1')

$btn.click(function() {
    if (fsm.is('收藏')) {
        fsm.doStore()
    } else {
        fsm.deleteStore()
    }
})

// 更新按钮文案
function updateText() {
    $btn.text(fsm.state)
}

// 初始化文案
updateText()
  • 写一个简单的promise
查看代码
js

import StateMachine from 'javascript-state-machine'

// 初始化状态模型

let fsm = new StateMachine({
    init: 'pending',
    transitions: [
        {
            name: 'resolve',
            from: 'pending',
            to: 'fullfilled'
        },
        {
            name: 'reject',
            from: 'pending',
            to: 'rejected'
        }
    ],
    methods: {
        // 监听执行收藏
        onResolve: function(state, data) {
            // state 当前状态机实例, data fsm.resolve(xxx) 传递的参数
            data.successList.forEach(fn => fn())
        },
        // 监听取消收藏
        onReject: function(state, data) {
            data.failList.forEach(fn => fn())
        }
    }
})

class MyPromise {
    constructor(fn) {
        this.successList = []
        this.failList = []

        fn(
            function() {
                fsm.resolve(this)
            },
            function() {
                fsm.reject(this)
            }
        )
    }
    then(successFn, failFn) {
        this.successList.push(successFn)
        this.failList.push(failFn)
    }
}

const src = 'https://forguo.cn/favicon.ico'

const loadImg = src => {
    const promise = new MyPromise(function(resolve, reject) {
        const img = document.createElement('img')
        img.onload = function() {
            resolve(img)
        }
        img.onerror = function() {
            reject()
        }
        img.src = src
    })
    return promise
}

const result = loadImg(src)

result.then(
    function() {
        console.log('ok1')
    },
    function() {
        console.log('fail1')
    }
)

result.then(
    function() {
        console.log('ok2')
    },
    function() {
        console.log('fail2')
    }
)

4、策略模式

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

5、模板方法模式

  • 一步操作可能分为多个职责角色来完成
  • 把这些角色都分开,然后再用一个统一的方法来调用,这个统一的方法就是模板方法
  • 将发起者和各个职责角色解耦

6、职责连模式

介绍

  • jQuery链式操作,Promise.then 的链式操作

7、命令模式

  • 执行命令时,发布者和执行者分开
  • 中间加入命令对象,作为中转站
  • JS中的Promise

8、备忘录模式

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

// 备忘录模式
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()) // 111

9、中介者模式

  • 一个对象不可能与其他所有对象发生紧耦合关系,这样对象之间的网会非常复杂
  • 中介者模式将网状结构分离为星型结构

10、访问者模式

  • 将数据操作和数据结构进行分离
  • JS中使用较少

11、解释器模式

  • 描述语言语法如何定义,如何解释和编译
  • 用于专业场景