浅谈前端设计模式:不常用设计模式

89 阅读5分钟

23种设计模式

  • 创建型 工厂模式(工厂方法模式、抽象工厂模式、建造者模式)、单例模式、原型模式
  • 组合型 适配器、装饰器、代理、外观、桥接、组合、享元
  • 行为型 策略、模板方法、观察者、迭代器、职责链、命令、备忘录、状态、访问者、中介者、解释器

前面我们已经分别详细讲了前端的一些学用设计模式,但是不常用的设计模式我们要需要了解一下。下面就大概讲一下其它不常用设计模式。


一、原型模式

1、概念

clone自已,生成一个新对象。(java默认有clone接口,不用自己实现)

2、应用

JS中的的应用: Object.create会以某个对象为原型创建新对象。

// 基于一个原型创建一个对象
const prototype = {
    getName: function () {
        return this.first + ' ' + this.last
    },
    say: function () {
        console.log('hello, I am ' + this.getName())
    }
}

// 基于原型创建x
let x = Object.create(prototype)
x.first = 'A'
x.last = 'B'
x.say() // => hello, I am A B

// 基于原型创建x
let y = Object.create(prototype)
y.first = 'C'
y.last = 'D'
y.say() // => hello, I am C D

JS中的原型prototype

JavaScript 继承机制的设计思想就是,原型对象的所有属性和方法,都能被实例对象共享。也就是说,如果属性和方法定义在原型上,那么所有实例对象就能共享,不仅节省了内存,还体现了实例对象之间的联系。

原型prototype的代码示例如下:

function Animal(name) {
  this.name = name;
}
Animal.prototype.color = 'white';

var cat1 = new Animal('大毛');
var cat2 = new Animal('二毛');

cat1.color // 'white'
cat2.color // 'white'

JS中的面向对象就是基于prototype实现的。ES6中的class的底层原型其实就是prototype。


二、桥接模式

1、概念

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

2、示例讲解

只看概念有点不好理解,我们以一个示例为讲解。现在有一个需求,需要画不同形状不同颜色的图形。

第一种做法是: 每一个图形都单独使用一个方法画形状和颜色。复用性不高。 image.png

image.png

第二种做法是: 使用两个类(形状类、颜色类)把形状和颜色解耦。分别维护形状和颜色,复用性更高。 image.png

image.png


三、组合模式

1、概念

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

2、示例讲解

image.png

3、应用

  • 虚拟DOM中的vnode是这种形式

image.png

  • 业务逻辑中的多级菜单也是这种思想

四、享元模式

1、概念

享元两个字应该先分开解释。享是共享,元是元数据。

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

2、示例演示

该示例适用于代理模式,也适用于享元模式。

image.png


五、责任链模式

1、概念

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

2、示例

请假审批,需要组长审批、经理审批、最后总监审批

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()
        }
    }
}

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

3、JS中的链式操作

职责链模式和业务结合较多,JS中类似的能联想链式操作。 jQuery的链式操作、Promise.then的链式操作。


六、模板方法模式

1、概念

模板方法模式实际上封装了一个固定流程,该流程有几个步骤组成,具体步骤可以由子类进行不同的实现。

2、示例

image.png


七、命令模式

1、概念

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

image.png

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
    }
    invoke() {
        console.log('开始')
        this.command.cmd()
    }
}

// 士兵
let soldier = new Receiver()

// 传令官
let herald = new Command(soldier)

// 将军
let general = new Invoker(trumpeter)
general.invoke(()

3、JS中的应用

页面富文本编辑器操作,浏览器封装了一个命令对象

document.execCommand('bold')  // 加粗

document.execCommand('undo') // 撤销

4、命令模式和发布订阅模式的区别

命令模式偏向于对一些琐碎 API 的封装,封装到一个命令对象,执行一个命令 API 。


八、备忘录模式

1、概念

  • 随时记录一个对象的状态变化;
  • 随时可经恢复之前的某人状态(如撤销功能);
// m1.js
// 备忘录类
class Memento {
    constructor(content) {
        this.content = content
    }
    getContent () {
        return this.content
    }
}

// 备忘录列表管理类
class CareTaker {
    constructor() {
        this.list = []
    }

    save (memento) {
        this.list.push(memento)
    }

    restore (index) {
        return this.list[index]
    }
}

// 编辑器类
class Editor {
    constructor() {
        this.content = ''
        this.careTaker = new CareTaker()
    }

    setContent (content) {
        this.content = content
    }

    getContent () {
        return this.content
    }

    save () {
        this.careTaker.save(new Memento(this.content))
    }

    restore (index) {
        const memento = this.careTaker.restore(index)
        return memento && memento.getContent()
    }
}

export default Editor
// index.js
import Editor from './m1'

const editor = new Editor()

editor.setContent('111')
editor.setContent('222')
editor.save()
editor.setContent('333')
editor.save()
editor.setContent('444')

console.log(editor.getContent()) // 444
console.log(editor.restore(2)) // undefined
console.log(editor.restore(1)) // 333
console.log(editor.restore(0)) // 222


九、中介者模式

1、概念

中介者模式是用来降低多个对象和类之间的通信复杂性。这种模式提供了一个中介类,该类通常处理不同类之间的通信,并支持松耦合,使代码易于维护。

image.png

意图: 用一个中介对象来封装一系列的对象交互,中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。

主要解决: 对象与对象之间存在大量的关联关系,这样势必会导致系统的结构变得很复杂,同时若一个对象发生改变,我们也需要跟踪与之相关联的对象,同时做出相应的处理。

2、代码演示

image.png


十、解释器模式

概念

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

十一、访问者模式

概念

将数据操作和数据结构进行分离。使用场景很少。