状态模式详解(角色作用以及应用案例)

状态模式详解(角色作用以及应用案例)-mikechen

状态模式定义

状态模式,英文名State Pattern,它允许一个对象其内部状态改变时改变它的行为,它能够通过调用模式接口中定义的方法来切换策略。

状态模式是一种行为模式,用于封装同一对象的不同行为,可以是对象在运行时更改其行为的更简洁的方式,而无需求助于条件语句,从而提高可维护性。

 

状态模式作用

将业务模型抽象成一个有限状态机,减少大量的switch-case和if-else的使用,使得代码逻辑清晰,结构规整。

 

状态模式角色

状态模式包含以下主要角色,如下图所示:

状态模式详解(角色作用以及应用案例)-mikechen

1.环境类Context角色

环境类Context:也称为上下文,它定义了客户端需要的接口,内部维护一个当前状态,并负责具体状态的切换。

 

2.抽象状态State角色

定义一个接口,用以封装环境对象中的特定状态所对应的行为,可以有一个或多个行为。

 

3.具体状态Concrete State角色

实现抽象状态所对应的行为,并且在需要的情况下进行状态切换。

 

 

状态模式案例

可以用用“状态模式”设计一个多线程的状态转换程序,多线程存在 5 种状态:分别为新建状态、就绪状态、运行状态、阻塞状态和死亡状态。

各个状态当遇到相关方法调用,或事件触发时会转换到其他状态转换。

如下图所示:

状态模式详解(角色作用以及应用案例)-mikechen

在先定义一个抽象状态类(TheadState),然后为图 3 所示的每个状态设计一个具体状态类,它们是新建状态(New)、就绪状态(Runnable )、运行状态(Running)、阻塞状态(Blocked)和死亡状态(Dead),每个状态中有触发它们转变状态的方法,环境类(ThreadContext)中先生成一个初始状态(New),并提供相关触发方法,图 4 所示是线程状态转换程序的结构图。

状态模式详解(角色作用以及应用案例)-mikechen

代码示例如下:

第一步:创建环境类(Context角色)

//环境类
class ThreadContext {
    private ThreadState state;
    ThreadContext() {
        state = new New();
    }
    public void setState(ThreadState state) {
        this.state = state;
    }
    public ThreadState getState() {
        return state;
    }
    public void start() {
        ((New) state).start(this);
    }
    public void getCPU() {
        ((Runnable) state).getCPU(this);
    }
    public void suspend() {
        ((Running) state).suspend(this);
    }
    public void stop() {
        ((Running) state).stop(this);
    }
    public void resume() {
        ((Blocked) state).resume(this);
    }
}

 

第二步:创建抽象状态(State角色)

//抽象状态类:线程状态
abstract class ThreadState {
    protected String stateName; //状态名
}

 

第三步:创建具体状态(Concrete State角色)

一共分为5大状态,如下所示:

//具体状态类:新建状态
class New extends ThreadState {
    public New() {
        stateName = "新建状态";
        System.out.println("当前线程处于:新建状态.");
    }
    public void start(ThreadContext hj) {
        System.out.print("调用start()方法-->");
        if (stateName.equals("新建状态")) {
            hj.setState(new Runnable());
        } else {
            System.out.println("当前线程不是新建状态,不能调用start()方法.");
        }
    }
}
//具体状态类:就绪状态
class Runnable extends ThreadState {
    public Runnable() {
        stateName = "就绪状态";
        System.out.println("当前线程处于:就绪状态.");
    }
    public void getCPU(ThreadContext hj) {
        System.out.print("获得CPU时间-->");
        if (stateName.equals("就绪状态")) {
            hj.setState(new Running());
        } else {
            System.out.println("当前线程不是就绪状态,不能获取CPU.");
        }
    }
}
//具体状态类:运行状态
class Running extends ThreadState {
    public Running() {
        stateName = "运行状态";
        System.out.println("当前线程处于:运行状态.");
    }
    public void suspend(ThreadContext hj) {
        System.out.print("调用suspend()方法-->");
        if (stateName.equals("运行状态")) {
            hj.setState(new Blocked());
        } else {
            System.out.println("当前线程不是运行状态,不能调用suspend()方法.");
        }
    }
    public void stop(ThreadContext hj) {
        System.out.print("调用stop()方法-->");
        if (stateName.equals("运行状态")) {
            hj.setState(new Dead());
        } else {
            System.out.println("当前线程不是运行状态,不能调用stop()方法.");
        }
    }
}
//具体状态类:阻塞状态
class Blocked extends ThreadState {
    public Blocked() {
        stateName = "阻塞状态";
        System.out.println("当前线程处于:阻塞状态.");
    }
    public void resume(ThreadContext hj) {
        System.out.print("调用resume()方法-->");
        if (stateName.equals("阻塞状态")) {
            hj.setState(new Runnable());
        } else {
            System.out.println("当前线程不是阻塞状态,不能调用resume()方法.");
        }
    }
}
//具体状态类:死亡状态
class Dead extends ThreadState {
    public Dead() {
        stateName = "死亡状态";
        System.out.println("当前线程处于:死亡状态.");
    }
}

 

第四步:测试状态转换

public class ScoreStateTest {
    public static void main(String[] args) {
        ThreadContext context = new ThreadContext();
        context.start();
        context.getCPU();
        context.suspend();
        context.resume();
        context.getCPU();
        context.stop();
    }
}

运行结果如下:

当前线程处于:新建状态.
调用start()方法-->当前线程处于:就绪状态.
获得CPU时间-->当前线程处于:运行状态.
调用suspend()方法-->当前线程处于:阻塞状态.
调用resume()方法-->当前线程处于:就绪状态.
获得CPU时间-->当前线程处于:运行状态.
调用stop()方法-->当前线程处于:死亡状态.

 

状态模式应用

通常在以下情况下可以考虑使用状态模式,大致分为两类:

第一类:当一个对象的行为取决于它的状态,并且它必须在运行时根据状态改变它的行为时,就可以考虑使用状态模式;

第二类:一个操作中含有庞大的分支结构,并且这些分支决定于对象的状态时。

陈睿mikechen

10年+大厂架构经验,资深技术专家,就职于阿里巴巴、淘宝、百度等一线互联网大厂。

关注「mikechen」公众号,获取更多技术干货!

后台回复面试即可获取《史上最全阿里Java面试题总结》,后台回复架构,即可获取《阿里架构师进阶专题全部合集

评论交流
    说说你的看法