(十二)状态模式

232 阅读2分钟

@TOC

状态模式

  • 介绍
  • 演示
  • 场景
  • 总结

状态模式 介绍

  • 一个对象有状态变化

  • 每次状态变化都会触发一个逻辑

  • 不能总是用if...else来控制

概念

一个对象或者实例可能会有不同状态的变化(例如交通信号灯),如果仅用if...else去处理状态变化,扩展性非常差,特别是针对复杂的状态变化。状态模式就提出了一种处理复杂状态变化且扩展性好的设计思路。

示例

  • 交通信号灯不同颜色的变化
  • 状态模式 演示

传统的 UML 类图

在这里插入图片描述

简化之后的 UML 类图

在这里插入图片描述

代码演示

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
    }
    setState(state) {
        this.state = state
    }
    getState() {
        return this.state
    }
}

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

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

// 绿灯亮了
greed.handle(context)
console.log(context.getState())
// 黄灯亮了
yellow.handle(context)
console.log(context.getState())
// 红灯亮了
red.handle(context)
console.log(context.getState())

状态模式 场景

有限状态机(finite state machine)

表示有限个状态以及在这些状态之间的转移和动作等行为的数学模型,例如上一节的交通信号灯的状态变化。

有一个开源 lib javascript-state-machine 实现了有限状态机的功能。我们使用它来模拟一个比较常用的操作,“收藏”和“取消收藏”

// 状态机模型
var fsm = new StateMachine({
    init: '收藏',  // 初始状态,待收藏
    transitions: [
        {
            name: 'doStore',
            from: '收藏',
            to: '取消收藏'
        },
        {
            name: 'deleteStore',
            from: '取消收藏',
            to: '收藏'
        }
    ],
    methods: {
        // 执行收藏
        onDoStore: function () {
            alert('收藏成功')
            updateText()
        },
        // 取消收藏
        onDeleteStore: function () {
            alert('已取消收藏')
            updateText()
        }
    }
})

var $btn = $('#btn')

// 点击事件
$btn.click(function () {
    if (fsm.is('收藏')) {
        fsm.doStore()
    } else {
        fsm.deleteStore()
    }
})

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

// 初始化文案
updateText()

实现一个简单的 Promise

先回归一下 Promise 的语法

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

var src = 'http://www.imooc.com/static/img/index/logo_new.png'
var result = loadImg(src)

result.then(function (img) {
    console.log('success 1')
}, function () {
    console.log('failed 1')
})
result.then(function (img) {
    console.log('success 2')
}, function () {
    console.log('failed 2')
})

Promise 就是一个有限状态机,有三个状态pending fullfilled rejected,其中的变化必须是pending -> fullfilled 或者pending -> rejected,不能逆向变化,这样就可以根据有限状态即实现一个基本的 Promise

// 模型
var 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) {
            // 参数:state - 当前状态示例; data - fsm.reject(xxx) 执行时传递过来的参数
            data.failList.forEach(fn => fn())
        }
    }
})

// 定义 Promise
class MyPromise {
    constructor(fn) {
        this.successList = []
        this.failList = []

        fn(() => {
            // resolve 函数
            fsm.resolve(this)
        }, () => {
            // reject 函数
            fsm.reject(this)
        })
    }
    then(successFn, failFn) {
        this.successList.push(successFn)
        this.failList.push(failFn)
    }
}

然后测试一下

// 测试代码
function loadImg(src) {
    const promise = new MyPromise(function (resolve, reject) {
        var img = document.createElement('img')
        img.onload = function () {
            resolve(img)
        }
        img.onerror = function () {
            reject()
        }
        img.src = src
    })
    return promise
}
var src = 'http://www.imooc.com/static/img/index/logo_new.png'
var result = loadImg(src)
console.log(result)

result.then(function (img) {
    console.log('success 1')
}, function () {    
    console.log('failed 1')
})
result.then(function (img) {
    console.log('success 2')
}, function () {    
    console.log('failed 2')
})

注意,本节以讲解状态模式位置,仅实现了简单的 Promise 原型,Promise 其他高级功能大家可自行去扩展

状态模式 总结

  • 状态模式是什么
  • 主要解决什么问题
  • 核心:不同状态变化的逻辑,分离实现,而不是用if...else放在一起判断
  • 有限状态机

和观察者模式的对比:两者之间没有矛盾,而是状态模式中可以使用观察者模式。 - 状态模式:不同状态的变化带来的改变。类似于多种if..else的逻辑。 - 观察者模式:先订阅、后发布,触发订阅。类似于一次性触发多个事件,并没有if...else的判断。

设计原则验证:

  • 将状态对象和主题对象分离,状态的变化逻辑单独处理
  • 符合开放封闭原则