美文网首页
浅析设计模式-状态模式

浅析设计模式-状态模式

作者: RunAlgorithm | 来源:发表于2017-08-21 23:35 被阅读150次

定义

状态模式(State Pattern):允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类。其别名为状态对象(Objects for States),状态模式是一种对象行为型模式。

一个对象有多种状态的变化,然后不同的状态能够转换,不同的状态有不同的行为和数据,这时候可以使用状态模式来封装它。状态模式,会对每一种状态创建一个状态类,这些状态类管理当前状态和到下一个状态变换的数据、行为。

状态模式的重点在于,不同的状态对象中,有一个相同的行为或者事件发生时,如何进行处理,如何切换到其他状态

现实世界的模型

比如,人的情绪有状态,喜怒哀乐,不同的状态会随着某些事件而发生改变

状态模式有几个关键元素

  • 环境,就是那个有状态的对象,不同的状态有不同的行为和数据
  • 命令,环境对象的使用者发出,通知改变状态
  • 状态类,处理命令,去改变环境的行为和数据,也可以让环境进入下一个状态

为什么要使用状态模式?

我们在遇到状态切换的时候,如果使用 if else 或者 switch case 等方式类枚举

每个要做和状态相关的代码,都要把所有状态枚举一遍,这样会导致代码非常冗长

而且一旦有需求变化,需要对大量的代码进行修改,容易引发很多意想不到的问题

比如一个环境对象 Context 中有三个状态,A, B, C,然后响应两个命令 cmd1 和 cmd2,我们使用 switch case 来处理

简单的状态切换规则如下:

状态模式-简单例子-状态机.png

如果我们用 if else 的多重条件判断来实现这些状态转换,会有这样的代码:

public class Context {
    // 某些数据和行为
    ...
    
    enum State {
        A, B, C
    }
    
    private State state;

    public void cmd1() {
        switch state:
            case A:
                // do something
                state = B;
                break;
            case B:
                // do something
                state = C;
                break;
            default:
                break;
    }

    public void cmd2() {
        switch state:
            case A:
                // do something
                state = C;
                break;
            case C:
                // do something
                state = B;
                break;
            default:
                break;
    }
}

那么问题来了,如果再新增一个状态 D 呢?需要在各个命令方法的枚举中,继续枚举出 D,然后做修改;如果 装填A 要做修改呢,每个命令关于 A 的都要改过去?随便来一个需求,都需要改动许多地方,维护艰难且容易出错

我们的改进方法,就是建立状态类,把这些和各个状态相关的处理代码,内聚到这些状态类中。这里有三个状态,我们就这个环境中创建三个状态类,StateA,StateB,StateC,由状态类接管当前状态下环境对象的数据和行为变化

这样子才能更好地应对未来的变化,不论是新增状态、单个状态发生变化、还是新增命令,都可以减少改动量

public class Context {
    // ------- 一些数据 -------
    ...
    
    private State mState;
    
    // ------- 行为命令 -------
    
    public void cmd1() {
        mState.handle(1);
        
        // 状态切换代码,也可以让各个状态类来做切换
        if (mState instanceof StateA) {
            mState = new StateB();
        } else if (mState instanceof StateB) {
            mState = new StateC();
        }
    }
    
    public void cmd2() {
        mState.handle(2);
        
        if (mState instanceof StateA) {
            mState = new StateC();
        } else if (mState instanceof StateC) {
            mState = new StateB();
        }
    }
    
    // ------- 状态类 -------
    
    ...

    private interface State {
        handleCmd(int cmd);
    }
    
    private class StateA {
        public void handleCmd(int cmd) {
            switch(cmd) {
                case 1:
                    // do somethig
                    break;
                case 2:
                    // do somethig
                    break;
                default:
                    break;
            }
        }
    }
    
    private class StateB {
        public void handleCmd(int cmd) {
            switch(cmd) {
                case 1:
                    // do somethig
                    break;
                default:
                    break;
            }
        }
    }
    
    ...
    
}

新增状态 D 呢,再建一个 StateD,把 D 状态关心的环境数据变化和对应的状态切换加入,不用修改其他状态的代码,直接扩展;如果状态 A 发生变化呢,直接修改 StateA,不会影响到其他的状态类。各个状态类独立

这样子,就把各个状态的行为切换和数据变化内聚到一个个状态类中,每个状态类只关心自己能够响应的命令。某种状态调整,只需要修改该状态类,修改量小;新增状态,也只需要再实现一个状态类,符合开闭原则

简单设计

环境类

持有一个状态实例引用,拥有多个命令方法给外界调用,外界可以通过环境类,调用某些命令方法,来控制状态的变化

该环境内部有多个其他对象或者数据,可以随着状态的切换而发生变化,产生某些行为

具体状态的切换,可以在环境类中做,也可以放到具体的状态类中去做

抽象状态类

定义了状态的处理方法

如果状态切换要放到具体的实现类去完成,还可以定义状态切换方法

具体状态类

实现状态的处理方法

那之前的例子来说,我们可以得到这样的类图

状态模式-简单例子-类图.png

环境类就是 Context,抽象状态类就是 IState,具体状态类就是 StateA,StateB,StateC

应用实例

续播栏

需求背景:在视频播放详情页中,在视频播放结束的时候,有一个续播提示栏,用来做续播的倒计时提示。而这个倒计时提示有多个状态,随着外界的变化而变化。主要的状态有初始化、重置、开始、结束、取消等。而外界的变化有滑动页面、关闭屏幕、打开新窗口等等

可以得到这样的状态机

状态模式-续播栏-状态机.png

我的实现和那个简单例子基本一样。不过状态的切换代码,没有放在环境类中去实现,我这边直接放到了各个状态类中去执行了。这两种方式都可以。不过最好还是由环境类去做,减少不同状态类中的相互依赖

环境类 PlayNextViewHolder

内置一些 UI 对象,这些 UI 对象跟随着状态切换而发生相应的变化。

抽象状态类 IState

这里只定义了状态切换方法和一些状态码

因为状态的处理放到了每个状态类的构造方法中,从而不需要在抽象类中定义 handle 方法。这只是一种处理方式,当然也可以自己定义 handle,在 handle 中做状态的处理。

同样,状态切换方法这里是放到了具体的子类,也可以放回环境类 PlayNextViewHolder 中,也是可以的

我这里只是一种实现方式。模式是一种思想,可以有多种实现

interface IState {
    int STATE_INIT = 0x0;
    int STATE_RESET_YES = 0x1;
    int STATE_RESET_NO = 0x2;
    int STATE_START = 0x3;
    int STATE_STOP = 0x4;
    int STATE_CANCEL = 0x5;
    int STATE_FINISH = 0x6;
    void switchState(IState state);
}

具体状态类

这些都是 PlayNextViewHolder 的内部类,这样的话默认持有了 PlayNextViewHolder 的引用,同时可以访问 PlayNextViewHolder 的 UI 对象,根据状态的切换来操作这些 UI

比如 InitState

    private class InitState implements IState {

        public InitState() {
            stopCountDown();
            closePlayNext();
            mStateLogger.d("init");
        }

        @Override
        public void switchState(@State int state) {
            switch (state) {
                case STATE_RESET_YES:
                    mState = new ResetOKState();
                    break;
                case STATE_RESET_NO:
                    mState = new ResetNOState();
                    break;
                default:
                    break;
            }
        }
    }

又比如 FinishState

    private class FinishState implements IState {

        public FinishState() {
            stopCountDown();
            closePlayNext();
            mStateLogger.d("finish");
        }

        @Override
        public void switchState(@State int state) {
            switch (state) {
                case STATE_RESET_YES:
                    mState = new ResetOKState();
                    break;
                case STATE_RESET_NO:
                    mState = new ResetNOState();
                    break;
                default:
                    break;
            }
        }
    }

具体代码就不罗列了,和业务场景有关

重要的是领会其中对状态模式的应用

采取了这个模式了,以后对结束状态的某些对象有调整,就不用像开头那样,找到每一个修改对象的方法,一个个改过去。我们只要修改 FinishState 就行了,修改量大幅度降低

不过,状态模式单纯从代码上进行阅读并不容易,还是要附上状态图,比较好理解

相关文章

  • 浅析Java设计模式【1】——观察者

    前情内容 浅析Java设计模式【1】——观察者 浅析Java设计模式【2】——适配器 浅析Java设计模式【3】—...

  • 浅析Java设计模式【2】——适配器

    前情内容 浅析Java设计模式【1】——观察者 浅析Java设计模式【2】——适配器 浅析Java设计模式【3】—...

  • 浅析Java设计模式【3】——代理

    1. 前情内容 浅析Java设计模式【1】——观察者 浅析Java设计模式【2】——适配器 浅析Java设计模式【...

  • 浅析设计模式-状态模式

    定义 状态模式(State Pattern):允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的...

  • 关于 MVVM 设计模式

    本文主要浅析传统 MVP 设计模式与先进流行的 MVVM 设计模式的一些区别,以及简要分析 MVVM 设计模式的优...

  • 设计模式-状态模式

    设计模式-状态模式 设计模式 状态模式的关键是区分事物内部的状态

  • 浅析状态机设计模式

    背景 在需求开发的过程中,经常会遇到根据不同的情况作出不同的处理。最直接的就是if...else...。当场景特别...

  • 设计模式——状态模式

    设计模式——状态模式 在状态模式中,类的行为是基于它的状态改变的。这种类型的设计模式属于行为型模式。 优点: 减少...

  • 设计模式-状态设计模式

    1.定义 对于某个操作,由于其状态的不同,表现出的行为会不同(如遥控器进行音量增加的操作,在电视是开机状态下是可以...

  • 设计模式——状态模式

    前言 设计模式是指导一个程序猿以更好的姿态处理一些问题,而不再像刚学编程的我们,只会使用if-else分支语句,或...

网友评论

      本文标题:浅析设计模式-状态模式

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