状态模式

2019/09/18 设计模式 共 2689 字,约 8 分钟

状态模式

HeadFirst里面讲状态模式举了一个自动售卖机的例子,这本书用来理解设计模式可以说是非常棒的了。

定义

Allow an object to alter its behavior when its internal state changes.The object will appear to change its class. 当一个对象内在状态改变时允许其改变行为,这个对象看起来像改变了其类。

类图

图片来自网络

状态模式类图

角色结构

State——抽象状态角色:接口或抽象类,负责对象状态定义,并且封装环境角色以实现状态切换。 ConcreteState——具体状态角色:每一个具体状态必须完成两个职责:本状态的行为管理以及趋向状态处理,通俗地说,就是本状态下要做的事情,以及本状态如何过渡到其他状态。 Context——环境角色:定义客户端需要的接口,并且负责具体状态的切换。

实例

大家都知道一个线程的生命周期,从创建开始,是就绪状态(Runnable)->运行状态(Running)->终止(Terminated),如果在运行状态线程被阻塞,则会进入阻塞状态(Blocked),然后阻塞结束,线程重新进入就绪状态(Runnable)。
图片来源于:https://www.cnblogs.com/ChaosJu/p/4528895.html 线程状态转化图

  1. 创建线程状态抽象类
public abstract class ThreadState {

    /**
     * 环境角色,描述线程此时的状态
     */
    protected ThreadContext threadContext;

    public void setThreadContext(ThreadContext threadContext) {
        this.threadContext = threadContext;
    }

    /**
     * 线程在该状态下的操作
     */
    public abstract void handle();
}

  1. 创建具体的线程状态类,继承抽象类
public class StartState extends ThreadState {

    @Override
    public void handle() {
        System.out.println("线程创建,调用start()方法,状态转变为就绪状态");
        // 设置环境对象的状态为下一个就绪状态
        super.threadContext.setThreadState(ThreadContext.RUNNABLE);
    }
}

public class RunnableState extends ThreadState {
    @Override
    public void handle() {
        System.out.println("线程就绪状态,随时可以启动,同时状态转变为运行状态");
        super.threadContext.setThreadState(ThreadContext.RUNNING);
    }
}

public class RunningState extends ThreadState {

    @Override
    public void handle() {
        System.out.println("线程为运行状态,正在运行,同时转为下一个终止状态");
        super.threadContext.setThreadState(ThreadContext.Terminated);
    }
}

public class TerminatedState extends ThreadState {
    @Override
    public void handle() {
        System.out.println("线程已终止");
    }
}
  1. 创建环境状态,描述线程当前的状态,并调用handle方法
public class ThreadContext {

    /**
     * 创建状态
     */
    public static final ThreadState START = new StartState();
    /**
     * 就绪状态
     */
    public static final ThreadState RUNNABLE = new RunnableState();
    /**
     * 运行状态
     */
    public static final ThreadState RUNNING = new RunningState();
    /**
     * 终止状态
     */
    public static final ThreadState Terminated = new TerminatedState();

    /**
     * 定义线程此时的状态
     */
    private ThreadState threadState;
    public void setThreadState(ThreadState threadState) {
        this.threadState = threadState;
        this.threadState.setThreadContext(this);
    }

    /**
     * 线程在该状态下的操作
     */
    public void handle() {
        this.threadState.handle();
    }
}
  1. 创建测试类
public class Test {

    public static void main(String[] args) {
        ThreadContext threadContext = new ThreadContext();
        threadContext.setThreadState(new StartState());
        threadContext.handle();
        threadContext.handle();
        threadContext.handle();
        threadContext.handle();
    }
}
  • 测试结果为
线程创建,调用start()方法,状态转变为就绪状态
线程就绪状态,随时可以启动,同时状态转变为运行状态
线程为运行状态,正在运行,同时转为下一个终止状态
线程已终止

状态模式的优缺点

优点

  1. 结构清晰
    • 避免了过多的switch…case或者if…else语句的使用,避免了程序的复杂性,提高系统的可维护性。
  2. 遵循设计原则
    • 很好地体现了开闭原则和单一职责原则,每个状态都是一个子类,你要增加状态就要增加子类,你要修改状态,你只修改一个子类就可以了。
  3. 封装性非常好
    • 这也是状态模式的基本要求,状态变换放置到类的内部来实现,外部的调用不用知道类内部如何实现状态和行为的变换。

缺点

  1. 类膨胀

使用场景

  1. 行为随状态改变而改变的场景
  2. 条件、分支判断语句的替代者

注意事项

  • 状态模式适用于当某个对象在它的状态发生改变时,它的行为也随着发生比较大的变化,也就是说在行为受状态约束的情况下可以使用状态模式,而且使用时对象的状态最好不要超过5个。

源代码

GitHub地址

版权声明:本文为Planeswalker23所创,转载请带上原文链接,感谢。

文档信息

Search

    Table of Contents