JavaScript设计模式

前言

以下代码以及结合设计模式所做的案例请移步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)
设计原则
  • 各个关联对象通过中介者隔离
  • 符合开放封闭原则

你可能感兴趣的:(JavaScript设计模式)