美文网首页
设计模式之状态模式

设计模式之状态模式

作者: 回调的幸福时光 | 来源:发表于2019-06-14 18:06 被阅读0次

    一、基础介绍

    状态模式:允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类。

    • 将状态封装成独立的类,并将请求委托给当前状态对象。
    • 对象具有状态变化
    • 不同的状态,对应不同的行为。
    • 状态有共同的行为方法, Context 会将请求委托给状态对象的这些方法,但是行为方法的内部实现是不同的。
    状态模式 UML 类图

    二、例子讲解

    以 promise 为例,我们知道 promise 是对异步操作的封装,内部有 3 种状态,每种状态对应不同的行为。

    以下的代码采用状态模式实现了简单版的 promise 。
    注意 then 方法并未提供完整功能,没有继续返回 promise ,所以无法链式调用。

    思考步骤:

    • 通常 Promise 使用关键字 new 调用,所以可以用构造函数/类来模拟。
    • 根据 Promise/A+ 规范,Promise 需要一个 executor , executor 是一个函数,有两个参数,分别是 resolvereject
    • 构造函数需要做的事情
    • then 方法参数中的回调处理不是立刻调用,而是等异步操作完成之后才会执行
    • 状态变化不可逆
    //  有限状态机(Finite State Machine)
    const FSM = {
      pending: {
        name: 'pending',
        done() {
          this.state = FSM.pending;
          console.log('pending 中...');
        }
      },
      resolved: {
        name: 'resolved',
        done() {
          if (this.state.name !== 'pending') return;
          this.state = FSM.resolved;
          console.log('状态更改为 resolved');
          // 调用 onResolve
          this.onResolve();
        }
      },
      rejected: {
        name: 'rejected',
        done() {
          if (this.state.name !== 'pending') return;
          this.state = FSM.rejected;
          console.log('状态更改为 rejected');
          // 调用 onReject
          this.onReject();
        }
      }
    }
    
    class myPromise {
      constructor(executor) {
        // 初始化状态为 pendding
        this.state = FSM.pending;
        FSM.pending.done.call(this);
        
        // 异步操作回调函数初始化
        this.onResolve = Function.prototype;
        this.onReject= Function.prototype;
        
        // 执行 executor
        executor(() => {
          FSM.resolved.done.call(this);
        },() => {
          FSM.rejected.done.call(this);
        })
      }
    
      then(onResolve, onReject) {
        // 校验 onResolve
        if (onResolve  && typeof onResolve !== 'function') {
          throw new Error("出错啦~");
        }
        
        // 校验 onReject 
        if (onReject && typeof onReject !== 'function') {
          throw new Error("出错啦~");
        }
    
        this.onResolve = typeof onResolve === 'function' ? onResolve : () => {};
        this.onReject = typeof onReject=== 'function' ? onReject : () => { throw new Error("出错啦~");};
      }
    }
    

    测试代码:

     // 测试代码
    function ajax(type=true) {
      const promise = new myPromise((resolve, reject) => {
        setTimeout(() => {
          if (type) {
            resolve();
          } else {
            reject();
          }
        }, 1000);
      });
      return promise;
    }
    const response_1 = ajax();
    response_1.then(()=>{
        console.log('response_1 请求成功后的处理');
    },()=>{
        console.log('response_1 请求失败后的处理');
    });
    
    const response_2 = ajax(false);
    response_2.then(()=>{
        console.log('response_2 请求成功后的处理');
    },()=>{
        console.log('response_2 请求失败后的处理');
    });
    
    测试代码运行结果

    状态变更不可逆的处理

     if (this.state.name !== 'pending') return;
    
    状态变更是不可逆的

    github 上的 JavaScript 有限状态机库

    链接:jakesgordon/javascript-state-machine

    三、应用场景

    红绿灯、灯的开关、文件上传、游戏中任务的动作状态等。

    四、优缺点

    优点:

    • 避免多重条件分支语句
    • 状态模式定义了状态与行为之间的关系,并将它们封装在一个类里。易扩展,添加新的状态。
    • Context 中的请求动作状态类中封装的行为可以独立变化而互不影响

    缺点:

    • 编写多个状态类。
    • 逻辑分散在状态类中。

    五、状态模式中的性能优化

    1. state 对象的创建和销毁
    • 状态对象被需要时,才动态创建。
    • 开始就创建所有的状态对象。
    1. 利用享元模式,可以使个 Context 对象共享 state 对象。

    六、策略模式和状态模式的关系

    相同点:

    • 它们都有一个上下文、一些策略/状态类。上下文把请求委托给这些类来执行。

    不同点:

    • 策略模式重点在于封装不同的算法,算法之间没有强联系,可互相替换。
    • 状态模式重点在于封装状态,行为会放在状态内部,状态之间会发生变化。

    推荐阅读 设计模式之禅:策略模式VS状态模式

    参考

    《JavaScript 设计模式与开发实践》曾探
    《JavaScript 设计模式》张容铭
    Javascript设计模式系统讲解与应用

    相关文章

      网友评论

          本文标题:设计模式之状态模式

          本文链接:https://www.haomeiwen.com/subject/ipqpfctx.html