前言
以下代码以及结合设计模式所做的案例请移步github:
https://github.com/webxing/design_patterns
设计原则
单一职责原则
- 一个程序只做好一件事
- 如果功能过于复杂就拆分开,每个部分保持独立
开放封闭原则
- 对扩展开放,对修改封闭
- 增加需求时,扩展新代码,而非修改已有代码
- 软件设计的终极目标
李氏置换原则
- 子类能覆盖父类
- 父类出现的地方子类就能出现
接口独立原则
- 保持接口的单一原则,避免出现"胖接口"
- 类似于单一职责原则,这里更关注接口
依赖倒置原则
- 面向接口编程,依赖于抽象而不依赖于具体
- 使用方只关注接口而不关注具体类的实现
工厂模式
- 将 new 操作单独封装
- 遇到 new 时,就要考虑是否该使用工厂模式
示例
- 去买饭,直接点餐,取餐,不会自己亲手做
- 饭店要封装做饭的工作,做好直接给买者
UML类图
代码
class Product {
constructor(name) {
this.name = name
}
init() {
console.log('init')
}
fn1() {
console.log('fn1')
}
fn2() {
console.log('fn2')
}
}
class Creator {
create(name) {
return new Product(name)
}
}
let creator = new Creator()
let p = creator.create('兰州拉面')
p.init()
p.fn1()
设计原则
- 构造函数与创建者分离
- 符合开放封闭原则
单例模式
- 系统中被唯一使用
- 一个类只有一个实例
示例
- 登录框
- 购物车
- jquery
- vuex中的store
代码
class LoginForm {
constructor() {
this.state = 'hide'
}
show() {
if (this.state === 'show') {
console.log('has been show')
return
}
this.state = 'show'
console.log('show success')
}
hide() {
if (this.state === 'hide') {
console.log('has been hide')
return
}
this.state = 'hide'
console.log('hide success')
}
}
LoginForm.getInstance = (function() {
let instance = null
return function() {
if (!instance) {
instance = new LoginForm()
}
return instance
}
})()
let login1 = LoginForm.getInstance()
login1.show()
let login2 = LoginForm.getInstance()
login2.hide()
console.log(login1 === login2)
设计原则
- 符合单一职责原则,只实例化唯一的对象
- 没有很好的体现开放封闭原则,但是没有违反开放封闭原则
适配器模式
- 旧接口格式和使用者不兼容
- 中间加一个适配转换接口
- 提供一个不同的接口
示例
- vue的computed
UML类图
代码
// 适配器模式
class Adapter {
specialRequest() {
return '香港电压'
}
}
class Target {
constructor() {
this.adapter = new Adapter()
}
request() {
let info = this.adapter.specialRequest()
return `${info} --转换器-- 中国电压`
}
}
let target = new Target()
let res = target.request()
console.log(res)
设计原则
- 将旧接口和使用者进行分离
- 符合开放封闭原则
代理模式
- 使用者无权访问目标对象
- 中间加代理,通过代理做授权和控制
- 提供一模一样的接口
- 显示原有功能,但是经过限制或者阉割之后的
示例
- 访问github
- 经纪人
UML类图
代码
// 代理模式
class RealImg {
constructor(fileName) {
this.fileName = fileName
this.loadFromDisk() // 模拟加载图片
}
display() {
console.log('display...' + this.fileName)
}
loadFromDisk() {
console.log('loadFromDisk...' + this.fileName)
}
}
class ProxyImg {
constructor(fileName) {
this.realImg = new RealImg(fileName)
}
display() {
this.realImg.display()
}
}
let proxyImg = new ProxyImg('1.png')
proxyImg.display()
// ES6 经纪人
let star = {
name: 'lx',
age: 22,
phone: 'star: 166111100006'
}
let agent = new Proxy(star, {
get: function(target, key) {
if (key === 'phone') {
return 'agent: 1661786543'
}
if (key === 'price') {
return 200000
}
return target[key]
},
set: function(target, key, value) {
if (key === 'customPrice') {
if (value < 200000) {
console.log('too Low')
} else {
target[key] = value
return true
}
}
}
})
console.log(agent.name)
console.log(agent.age)
console.log(agent.phone)
console.log(agent.price)
agent.customPrice = 300000
console.log(agent.customPrice)
设计原则
- 代理类和目标类分离,隔离开目标类和使用者
- 符合开放封闭原则
装饰器模式
- 为对象添加新功能
- 不改变原有的结构和功能
- 扩展功能,原有功能不变且可直接使用
示例
- 手机壳
UML类图
代码
// 装饰器模式
class Circle {
draw() {
console.log('画一个圆形')
}
}
class Decorator {
constructor(circle) {
this.circle = circle
}
draw() {
this.circle.draw()
this.setRedBorder(circle)
}
setRedBorder(circel) {
console.log('设置红色边框')
}
}
let circle = new Circle()
let decorator = new Decorator(circle)
decorator.draw()
// ES7 装饰器
@decoratorA
class A{}
function decoratorA(target) {
target.isDec = true
}
console.log(A.isDec)
@decoratorB(false)
class B{}
function decoratorB(isDec) {
return function(target) {
target.isDec = isDec
}
}
console.log(B.isDec)
// mixin装饰类
function mixin(...list) {
return function(target) {
Object.assign(target.prototype, ...list)
}
}
const Foo = {
foo() {
console.log('foo')
}
}
@mixin(Foo)
class MyClass {}
let obj = new MyClass()
obj.foo()
// readonly
function readonly(target, name, descriptor) {
console.log(target, name, descriptor)
descriptor.writable = false
return descriptor
}
class Person {
constructor() {
this.first = '刘'
this.last = '昕'
}
@readonly
name() {
console.log( `${this.first} ${this.last}` )
}
}
let person = new Person()
person.name()
// person.name = function() {}
console.log(person)
// log
function log(target, name, descriptor) {
console.log(target, name, descriptor)
let oldValue = descriptor.value
descriptor.value = function() {
console.log(`calling f ${name}... `, arguments)
return oldValue.apply(this, arguments)
}
return descriptor
}
class Math {
@log
add(a, b) {
return a + b
}
}
let math = new Math()
let addRes = math.add(1, 2)
console.log(addRes)
// core-decorators
import {readonly} from 'core-decorators'
class C {
@readonly
name() {
console.log('lx')
}
}
let c = new C()
c.name()
// c.name() = function(){}
设计原则
- 将现有对象和装饰器进行分离,两者独立存在
- 符合开放封闭原则
观察者模式
- 发布订阅
- 一对多
示例
- 点咖啡,点好之后坐等被叫
- 网页事件绑定
- Promise
- jQuery Callbacks
- nodejs 自定义事件
UML类图
代码
// 观察者模式
class Subject {
constructor() {
this.state = 0
this.observers = []
}
getState() {
return this.state
}
setState(state) {
this.state = state
this.notifyObservers()
}
notifyObservers() {
this.observers.forEach(observer => {
observer.update()
})
}
attach(observer) {
this.observers.push(observer)
}
}
class Observer {
constructor(name, subject) {
this.name = name
this.subject = subject
this.subject.attach(this)
}
update() {
console.log(this.name + ' has been update,' + this.subject.getState())
}
}
let subject = new Subject()
for(let i = 0; i< 10; i++) {
new Observer(i, subject)
}
subject.setState(10)
设计原则
- 主题和观察者分离,不是主动触发而是被动监听,两者解耦
- 符合开放封闭原则
迭代器模式
- 顺序访问一个集合
- 使用者无需知道集合的内部结构(封装)
UML类图
代码
// 迭代器模式
class Iterator {
constructor(container) {
this.list = container.list
this.index = 0
}
next() {
if (this.hasNext()) {
return this.list[this.index++]
}
return null
}
hasNext() {
if (this.index >= this.list.length) {
return false
}
return true
}
}
class Container {
constructor(list) {
this.list = list
}
getIterator() {
return new Iterator(this)
}
}
let arr = [1, 2, 3]
let container = new Container(arr)
let iterator = container.getIterator()
while(iterator.hasNext()) {
console.log(iterator.next())
}
// ES6 Iterator
let iterator = arr[Symbol.iterator]()
let item = { done: false}
while (!item.done) {
item = iterator.next()
if(item.value) {
console.log(item.value)
}
}
// for..of 遍历迭代器
for (let item of arr) {
console.log(item)
}
设计原则
- 迭代器对象和目标对象分离
- 迭代器将使用者与目标对象隔离开
- 符合开放封闭原则
状态模式
- 一个对象有状态变化
- 每次状态变化都会触发一个逻辑
- 不能总是用if...else...来控制
示例
- 有限状态机
- 简单的promise
UML类图
代码
// 状态模式
class State {
constructor(color) {
this.color = color
}
handle(context) {
console.log('turn to ' + this.color)
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')
green.handle(context)
console.log(context.getState())
// 有限状态机
import StateMachine from 'javascript-state-machine'
let fsm = new StateMachine({
init: '收藏',
transitions: [
{
name: 'doStore',
from: '收藏',
to: '取消收藏'
},
{
name: 'deleteStore',
from: '取消收藏',
to: '收藏'
}
],
methods: {
onDoStore() {
console.log('收藏了')
updateText()
},
onDeleteStore() {
console.log('取消收藏了')
updateText()
}
}
})
let dom = document.querySelector('p')
dom.onclick = function() {
if (fsm.is('收藏')) {
fsm.doStore()
} else {
fsm.deleteStore()
}
}
function updateText() {
dom.innerHTML = fsm.state
}
updateText()
// 有限状态机 promise
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(state, data) {
data.successList.forEach(fn => fn())
},
onReject(state, data) {
data.failList.forEach(fn => fn())
}
}
})
class MyPromise {
constructor(fn) {
this.successList = []
this.failList = []
fn(() => {
fsm.resolve(this)
}, () => {
fsm.reject(this)
})
}
then(successFn, failFn) {
this.successList.push(successFn)
this.failList.push(failFn)
}
}
function loadImg(src) {
const promise = new MyPromise(function(resolve, reject) {
let img = document.createElement('img')
img.onload = function() {
resolve(img)
}
img.onerror = function() {
reject()
}
img.src = src
})
return promise
}
let result = loadImg('https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1535887490282&di=8e030a963f1cdf657713aedfe0eda25f&imgtype=0&src=http%3A%2F%2Fnpic7.fangtoo.com%2Fcn%2Fzixun%2Fzh-chs%2F2017-10%2F27%2F354172-2017102710051570.jpg')
result.then(function() {
console.log('加载成功')
}, function() {
console.log('加载失败')
})
设计原则
- 将状态对象和主体对象分离,状态的变化逻辑单独处理
- 符合开放封闭原则
原型模式
- clone自己,生成一个新对象
示例
- Object.create()
代码
// 原型模式
const prototype = {
getName: function() {
return this.first + ' ' + this.last
},
say: function() {
console.log('hello')
}
}
let x = Object.create(prototype)
x.first = 'X'
x.last = 'x'
console.log(x.getName())
let y = Object.create(prototype)
y.first = 'Y'
y.last = 'y'
console.log(y.getName())
桥接模式
- 用于把抽象化与实现化解耦
- 使得两者可以独立变化
代码
// 桥接模式
class Draw {
constructor(type, color) {
this.type = type
this.color = color.name
}
draw() {
console.log(`draw a ${this.type} with ${this.color} color`)
}
}
class Color {
constructor(name) {
this.name = name
}
}
let red = new Color('red')
let blue = new Color('blue')
let rect = new Draw('rect', red)
let circ = new Draw('circ', blue)
rect.draw()
circ.draw()
设计原则
- 抽象和实现分离,解耦
- 符合开放封闭原则
组合模式
- 生成树形结构,表示‘整体-部分’关系
- 让整体和部分都具有一致的操作方式
示例
- 虚拟DOM中的vnode
- js菜单
设计原则
- 将整体和单个节点的操作抽象出来
- 符合开放封闭原则
享元模式
- 共享内存(主要考虑内存,而非效率)
- 相同的数据共享使用
设计原则
- 将相同的部分抽象出来
- 符合开放封闭原则
策略模式
- 不同的策略分开处理
- 避免出现大量的if..else或者switch..case
代码
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()
设计原则
- 不同策略,分开处理,而不是混合到一起
- 符合开放封闭原则
职责链模式
- 一步操作可能分为多个职责角色来完成
- 把这些角色都分开,用一个链串起来
- 将发起者和各个处理者进行隔离
代码
// 职责链模式
class Action {
constructor(name) {
this.name = name
this.nextAction = null
}
setNextAction(action) {
this.nextAction = action
return 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).setNextAction(a3)
a1.handle()
设计原则
- 发起者与各个处理者进行隔离
- 符合开放封闭原则
命令模式
- 执行命令时,发布者和执行者分开
- 中间加入命令对象,作为中转站
示例
- 将军--吹号手--士兵
代码
// 命令模式
class Receiver {
exec() {
console.log('执行 exec')
}
}
class Command {
constructor(receiver) {
this.receiver = receiver
}
cmd() {
this.receiver.exec()
}
}
class Invoke {
constructor(command) {
this.command = command
}
exec() {
this.command.cmd()
}
}
let receiver = new Receiver()
let command = new Command(receiver)
let invoke = new Invoke(command)
invoke.exec()
设计原则
- 命令对象与执行对象分开,解耦
- 符合开放封闭原则
备忘录模式
- 随时记录一个对象的状态变化
- 随时可以恢复之前的某个状态(如撤销功能)
代码
// 备忘录模式
class Momento { // 备忘类
constructor(content) {
this.content = content
}
getContent() {
return this.content
}
}
class CareTaker { // 备忘列表
constructor() {
this.list = []
}
add(momento) {
this.list.push(momento)
}
get(index) {
return this.list[index]
}
}
class Editor {
constructor() {
this.content = null
}
setContent(content) {
this.content = content
}
getContent() {
return this.content
}
saveContentToMomento() {
return new Momento(this.content)
}
getContentFromMomento(momento) {
this.content = momento.getContent()
}
}
let editor = new Editor()
let caretaker = new CareTaker()
editor.setContent('1111')
editor.setContent('2222')
caretaker.add(editor.saveContentToMomento()) // 备份当前内容
editor.setContent('3333')
caretaker.add(editor.saveContentToMomento()) // 备份当前内容
editor.setContent('4444')
console.log(editor.getContent())
editor.getContentFromMomento(caretaker.get(1)) // 撤销
console.log(editor.getContent())
editor.getContentFromMomento(caretaker.get(0)) // 撤销
console.log(editor.getContent())
设计原则
- 状态对象与使用者分开,解耦
- 符合开放封闭原则
中介者模式
代码
// 中介者模式
class A1 {
constructor() {
this.number = 0
}
setNumber(num, m) {
this.number = num
if (m) {
m.setB()
}
}
}
class B1 {
constructor() {
this.number = 0
}
setNumber(num, m) {
this.number = num
if (m) {
m.setA()
}
}
}
class Mediator {
constructor(a, b) {
this.a = a
this.b = b
}
setA() {
let number = this.b.number
this.a.setNumber(number)
}
setB() {
let number = this.a.number
this.b.setNumber(number)
}
}
let a = new A1()
let b= new B1()
let mediator = new Mediator(a, b)
a.setNumber(1000, mediator)
console.log(a, b)
b.setNumber(100, mediator)
console.log(a, b)
设计原则
- 各个关联对象通过中介者隔离
- 符合开放封闭原则