JavaScript 设计模式 -- 其他模式

1. 其他模式

1.1 创建型

1.1.1 原型模式
  • 概念
    • clone 自己,生成一个对象
    • java 默认有 clone 接口,不用自己实现
  • JS 中的应用 - Object.create
// `Object.create` 用到了原型模式的思想(虽然不是 java 中的 clone)
// 基于一个原型创建一个对象
var prototype = {
    
    
	getName: function () {
    
    
		return this.first + ' ' + this.last
	},
	say: function () {
    
    
		console.log('hello')
	}
}

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

// 基于原型创建 y
var 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 却会长久存在

1.2 结构型

1.2.1 桥接模式
  • 概念
    • 用于把抽象化与实现化解耦
    • 使得二者可以独立变化
  • 演示
    在这里插入图片描述
  • 代码实现
class ColorShape {
    
    
	yellowCircle() {
    
    
		console.log('yellow circle')
	}
	redCircle() {
    
    
		console.log('red circle')
	}
	yellowTriangle() {
    
    
		console.log('yellow triangle')
	}
	redTriangle() {
    
    
		console.log('red triangle')
	}
}

// 测试
let cs = new ColorShape()
cs.yellowCircle()
cs.redCircle()
cs.yellowTriangle()
cs.redTriangle()
  • 演示
    在这里插入图片描述
  • 代码演示
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('triangle', yellow)
triangle.draw()
  • 设计原则验证
    • 抽象和实现分离,解耦
    • 符合开放封闭原则
1.2.2 组合模式
  • 概念
    • 生成树形结构,表示“整体-部分”关系
    • 让整体和部分都具有一致的操作方式
  • 示例
    在这里插入图片描述
  • 演示
    • JS 经典应用中,未找到这么复杂的数据类型
    • 虚拟 DOM 中的 vnode 是这种形式,但数据类型简单
    • 用 JS 实现一个菜单,不算经典应用,与业务相关
<div id="div1" class="container">
	<p>123</p>
	<p>456</p>
</div>
{
    
    
	tag: 'div',
	attr: {
    
    
		id: 'div1',
		className: 'container'
	},
	children: [
		{
    
    
			tag: 'p',
			attr: {
    
    },
			children: ['123']
		},
		{
    
    
			tag: 'p',
			attr: {
    
    },
			children: ['456']
		}
	]
}
  • 演示
    • 整体和单个节点的操作是一致的
    • 整体和单个节点的数据结构也保持一致
  • 设计原则验证
    • 将整体和单个节点的操作抽象出来
    • 符合开放封闭原则
1.2.3 享元模式
  • 概念
    • 共享内存(主要考虑内存,而非效率)
    • 相同的数据,共享使用
  • 演示
<!-- 无限下拉列表,将事件代理到高层节点上 -->
<!-- 如果都绑定到`<a>`标签,对内存开销太大 -->
<div id="div1">
	<a href="#">a1</a>
	<a href="#">a2</a>
	<a href="#">a3</a>
	<a href="#">a4</a>
	<!-- 无限下拉列表 -->
</div>

<script>
	var div1 = document.getElementById('div1')
	div1.addEventListener('click', function(e) {
     
     
		var target = e.target
		if (e.nodeName === 'A') {
     
     
			alert(target.innerHTML)
		}
	})
</script>
  • 设计原则验证
    • 将相同的部分抽象出来
    • 符合开放封闭原则
1.2.4 外观模式
  • 概念
    • 为子系统中的一组接口提供了一个高层接口
    • 使用者使用这个高层接口
  • 示例
    • 去医院看病,接待员去挂号】门诊、划价、取药

在这里插入图片描述

  • UML 类图
    在这里插入图片描述
  • 场景
function bindEvent(elem, type, selector, fn) {
    
    
	if (fn == null) {
    
    
		fn = selector
		selector = null
	}
// ******
}

// 调用
bindEvent(elem, 'click', '#div1', fn)
bindEvent(elem, 'click', fn)
  • 设计原则验证
    • 不符合单一职责原则和开放封闭原则,因此谨慎使用,不可滥用

1.3 行为型

1.3.1 策略模式
  • 概念
    • 不同策略分开处理
    • 避免出现大量 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 用户购买')
		}
	}
}

// 测试代码
var u1 = new User('ordinary')
u1.buy()
var u2 = new User('member')
u2.buy()
var u3 = new User('vip')
u3.buy()
class OrdinaryUser {
    
    
	buy() {
    
    
		console.log('普通用户购买')
	}
}
class MemberUser {
    
    
	buy() {
    
    
		console.log('会员用户购买')
	}
}
class VipUser {
    
    
	buy() {
    
    
		console.log('vip 用户购买')
	}
}

var u1 = new OrdinaryUser()
u1.buy()
var u2 = new MemberUser()
u2.buy()
var u3 = new VipUser()
u3.buy()
  • 设计原则验证
    • 不同策略,分开处理,而不是混合在一起
    • 符合开放封闭原则
1.3.2 模版方法模式
class Action {
    
    
	handle() {
    
    
		handle1()
		handle2()
		handle3()
	}
	handle1() {
    
    
		console.log('1')
	}
	handle2() {
    
    
		console.log('2')
	}
	handle3() {
    
    
		console.log('3')
	}
}
1.3.3 职责链模式
  • 概念
    • 一步操作可能分位多个职责角色来完成
    • 把这些角色都分开,然后用一个链串起来
    • 将发起者和各个处理者进行隔离
  • 演示
// 请假审批,需要组长审批、经理审批、最后总监审批
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() 
  • JS 中的链式操作
    • 职责链模式和业务结合较多,JS 中能联想到链式操作
    • jQuery 的链式操作 Promise.then 的链式操作
  • 设计原则验证
    • 发起者于各个处理者进行隔离
    • 符合开放封闭原则
1.3.4 命令模式
  • 概念
    • 执行命令时,发布者和执行者分开
    • 中间加入命令对象,作为中转站
  • 示例
    在这里插入图片描述
  • 演示
// 接收者
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 trumpeter = new Command(soldier)
// 将军
let general = new Invoker(trumpeter)
general.invoke()
  • JS 中的应用
    • 网页富文本编辑器操作,浏览器封装了一个命令对象
    • document.execCommand('bold')
    • document.execCommand('undo')
  • 设计原则验证
    • 命令对象于执行对象分开,解耦
    • 符合开放封闭原则
1.3.5 备忘录模式
  • 概念
    • 随时记录一个对象的状态变化
    • 随时可以恢复之前的某个状态(如撤销功能)
    • 未找到 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 {
    
    
	constuctor() {
    
    
		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())
editor.getContentFromMemento(careTaker.get(1)) // 撤销
console.log(editor.getContent())
editor.getContentFromMemento(careTaker.get(0)) // 撤销
console.log(editor.getContent())
  • 设计原则验证
    • 状态对象于使用者分开,解耦
    • 符合开放封闭原则
1.3.6 中介者模式
  • 概念
    在这里插入图片描述
  • 演示
// 中介者
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)
console.log(a.number, b.number)
b.setNumber(100)
console.log(a.number, b.number)
  • 设计原则验证
    • 讲各关联对象通过中介者隔离
    • 符合开放封闭原则
1.3.7 访问者模式
  • 概念
    • 将数据操作和数据结构进行分离
    • 使用场景不多
1.3.8 解释器模式
  • 概念
    • 描述语言语法如何定义,如何解释和编译
    • 用于专业场景

猜你喜欢

转载自blog.csdn.net/qq_43645678/article/details/115027404