聊聊Java设计模式-状态模式

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

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

一般用来实现状态机,而状态机常用在游戏、工作流引擎等系统的开发中:

状态机

有限状态机(Finite State Machine,FSM),状态机有三个组成部分:状态(State)、事件(Event)和动作(Action)。其中事件也叫作转移条件(Transition Condition),事件主要用于触发状态的转移及动作的执行,动作不是必须的,也可能只转移状态,不执行任何动作。

一、状态模式的介绍

状态模式又名状态对象(Objects for States),它是一种对象行为型模式。它的解决思想是当控制一个对象状态转换的条件表达式过于复杂时,把相关“判断逻辑”提取出来,用各个不同的类进行表示,系统处于哪种情况、直接使用相应的状态类对象进行处理。

1.1 状态模式的结构

在状态模式的结构中,通过实现抽象状态类的具体状态类来定义多个状态,每个状态类仅实现自己的逻辑,上下文类负责切换状态。其结构类图如下所示:

image-20220410152059585

  • State:抽象状态类,提供一个方法封装上下文对象的状态
  • ConcreteState1、ConcreteState2:具体状态类,继承抽象状态类,实现状态下的行为
  • Context:上下文类,负责对具体状态进行切换
  • Client:客户端,调用具体状态和上下文

1.2 状态模式的实现

首先是抽象状态类,具体代码如下:

public abstract class State {
    /**抽象业务方法,不同的具体状态可以有不同的实现*/
    public abstract void handle();
}

其次是实现抽象状态类的具体状态类

public class ConcreteState1 extends State{

    @Override
    public void handle(Context context) {
        System.out.println("进入ConcreteState1中~");
        context.setState(this);
    }

    @Override
    public String toString() {
        return "concreteState1";
    }
}
public class ConcreteState2 extends State{

    @Override
    public void handle(Context context) {
        System.out.println("进入ConcreteState2中~");
        context.setState(this);
    }

    @Override
    public String toString() {
        return "ConcreteState2";
    }
}

接下来是上下文类,维护当前状态,并负责具体状态的切换

public class Context {

    private State state;

    //设置初始状态为null
    public Context() {
        state = null;
    }
    //实现状态转换
    public void setState(State state) {
        this.state = state;
    }

    public State getState() {
        return state;
    }
}

客户端测试类

public class Client {
    public static void main(String[] args) {
        Context context = new Context();
        System.out.println("现在的状态是:" + context.getState());
        System.out.println("---------------------------------");
        State concreteState1 = new ConcreteState1();
        concreteState1.handle(context);
        System.out.println("现在的状态是:" + context.getState());
        System.out.println("---------------------------------");
        State concreteState2 = new ConcreteState2();
        concreteState2.handle(context);
        System.out.println("现在的状态是:" + context.getState());

    }
}

测试结果:

现在的状态是:null
---------------------------------
进入ConcreteState1中~
现在的状态是:concreteState1
---------------------------------
进入ConcreteState2中~
现在的状态是:ConcreteState2

二、状态模式的应用场景

状态模式的应用比较广泛,比如游戏中角色状态的转换、公文审批中的流转等等。

以下情况可以考虑使用状态模式:

  • 对象的行为依赖于它的某些属性值(状态),而且状态的改变将导致行为的变化
  • 代码中包含大量与对象状态有关的条件语句(if-else),这些条件语句的出现会导致代码的可维护性和灵活性变差。

三、状态模式实战

本案例中模拟营销活动审核状态流转场景,在一个活动的上线中是需要多个层级进行审核才能上线的(案例来源于《重学Java设计模式》)。如下图中可以看到流程节点中包括各个状态到下一个状态扭转的关联条件:

状态模式案例

因此在审批过程中就难免会包含很多条件语句的判断,长此以往,随着状态数量的增加,会增加代码的可维护性和可读性。下面就利用状态模式来实现多状态的审批过程,先来看看状态模式模型的结构:

image-20220410160208977

  • State:状态抽象类,定义所有状态的操作接口
  • CheckState、CloseState、DoingState...:具体状态类,各种状态的具体逻辑实现
  • StateHandler:状态处理类,相当于之前结构中提到的上下文类,负责对状态流程进行统一处理

具体代码

  1. 基本活动信息活动枚举状态
public class ActivityInfo {

    private String activityId;
    private String activityName;
    private Enum<Status> status;
    private Date beginTime;
    private Date endTime;

    //get\set\Constructor
}
public enum Status {
    Editing,
    Check,
    Pass,
    Refuse,
    Doing,
    Close,
    Open
}
  1. 活动业务处理
public class ActivityService {

    private static Map<String, Enum<Status>> statusMap = new ConcurrentHashMap<>();

    public static void init(String activityId, Enum<Status> initStatus) {
        ActivityInfo activityInfo = new ActivityInfo();
        activityInfo.setActivityId(activityId);
        activityInfo.setActivityName("测试活动");
        activityInfo.setStatus(initStatus);
        activityInfo.setBeginTime(new Date());
        activityInfo.setEndTime(new Date());
        statusMap.put(activityId, initStatus);
    }

    /**
     * 查询活动信息
     * @param activityId 活动ID
     * @return 查询后的活动信息
     */
    public static ActivityInfo queryActivityInfo(String activityId) {
        ActivityInfo activityInfo = new ActivityInfo();
        activityInfo.setActivityId(activityId);
        activityInfo.setActivityName("测试活动");
        activityInfo.setStatus(statusMap.get(activityId));
        activityInfo.setBeginTime(new Date());
        activityInfo.setEndTime(new Date());
        return activityInfo;
    }

    /**
     * 查询活动状态
     * @param activityId 活动ID
     * @return 查询后的活动状态
     */
    public static Enum<Status> queryActivityStatus(String activityId) {
        return statusMap.get(activityId);
    }

    public static synchronized void execStatus(String activityId, Enum<Status> beforeStatus, Enum<Status> afterStatus) {
        /*如果前后两个状态相同,直接返回*/
        if (!beforeStatus.equals(statusMap.get(activityId))) {
            return;
        }
        /*反之更新statusMap*/
        statusMap.put(activityId, afterStatus);
    }
}

2.活动返回格式

public class Result {

    private String code;
    private String info;
    
    //get/set
}
  1. 抽象状态类具体状态实现
public abstract class State {

    /**提审*/
    public abstract Result arraignment(String activityId, Enum<Status> currentStatus);
    /**撤审*/
    public abstract Result checkRevoke(String activityId, Enum<Status> currentStatus);
    /**审核通过*/
    public abstract Result checkPass(String activityId, Enum<Status> currentStatus);
    /**拒审*/
    public abstract Result checkRefuse(String activityId, Enum<Status> currentStatus);
    /**关闭*/
    public abstract Result close(String activityId, Enum<Status> currentStatus);
    /**开启活动*/
    public abstract Result open(String activityId, Enum<Status> currentStatus);
    /**活动中*/
    public abstract Result doing(String activityId, Enum<Status> currentStatus);

}
public class CheckState extends State {
    @Override
    public Result arraignment(String activityId, Enum<Status> currentStatus) {
        return new Result("0001", "提审后不能重复提审");
    }

    @Override
    public Result checkRevoke(String activityId, Enum<Status> currentStatus) {
        ActivityService.execStatus(activityId, Status.Check, Status.Editing);
        return new Result("0000", "活动审核撤销回编辑");
    }

    @Override
    public Result checkPass(String activityId, Enum<Status> currentStatus) {
        ActivityService.execStatus(activityId, Status.Check, Status.Pass);
        return new Result("0000", "活动审核通过");
    }

    @Override
    public Result checkRefuse(String activityId, Enum<Status> currentStatus) {
        ActivityService.execStatus(activityId, Status.Check, Status.Refuse);
        return new Result("0000", "活动审核被拒绝");
    }

    @Override
    public Result close(String activityId, Enum<Status> currentStatus) {
        return new Result("0001", "活动审核后不能直接关闭");
    }

    @Override
    public Result open(String activityId, Enum<Status> currentStatus) {
        return new Result("0001", "活动审核后不能再开启");
    }

    @Override
    public Result doing(String activityId, Enum<Status> currentStatus) {
        return new Result("0001", "活动审核不通过无法进入活动中");
    }
}
//下面依次是其他几个状态,较多省略
  1. 状态处理类,相当于前面的上下文,负责进行状态转移
public class StateHandler {

    private Map<Enum<Status>, State> stateMap = new ConcurrentHashMap<>();

    public StateHandler() {
        stateMap.put(Status.Check, new CheckState());
        stateMap.put(Status.Close, new CloseState());
        stateMap.put(Status.Doing, new DoingState());
        stateMap.put(Status.Refuse, new RefuseState());
        stateMap.put(Status.Pass, new PassState());
        stateMap.put(Status.Open, new OpenState());
        stateMap.put(Status.Editing, new EditingState());
    }

    public Result arraignment(String activityId, Enum<Status> currentStatus) {
        return stateMap.get(currentStatus).arraignment(activityId, currentStatus);
    }

    public Result checkPass(String activityId, Enum<Status> currentStatus) {
        return stateMap.get(currentStatus).checkPass(activityId, currentStatus);
    }

    public Result checkRefuse(String activityId, Enum<Status> currentStatus) {
        return stateMap.get(currentStatus).checkRefuse(activityId, currentStatus);
    }

    public Result checkRevoke(String activityId, Enum<Status> currentStatus) {
        return stateMap.get(currentStatus).checkRevoke(activityId, currentStatus);
    }

    public Result close(String activityId, Enum<Status> currentStatus) {
        return stateMap.get(currentStatus).close(activityId, currentStatus);
    }

    public Result open(String activityId, Enum<Status> currentStatus) {
        return stateMap.get(currentStatus).open(activityId, currentStatus);
    }

    public Result doing(String activityId, Enum<Status> currentStatus) {
        return stateMap.get(currentStatus).doing(activityId, currentStatus);
    }
}

5.测试类

public class ApiTest {

    private Logger logger = LoggerFactory.getLogger(ApiTest.class);

    @Test
    public void test_Check2Close() {
        String activityId = "100001";
        ActivityService.init(activityId, Status.Check);
        StateHandler stateHandler = new StateHandler();
        Result result = stateHandler.close(activityId, Status.Check);
        logger.info("测试结果(提审到关闭):{}", JSON.toJSONString(result));
        logger.info("活动信息:{} 状态:{}", JSON.toJSONString(ActivityService.queryActivityInfo(activityId)), JSON.toJSONString(ActivityService.queryActivityStatus(activityId)));
    }

    @Test
    public void test_Refuse2Revoke() {
        String activityId = "100001";
        ActivityService.init(activityId, Status.Refuse);

        StateHandler stateHandler = new StateHandler();
        Result result = stateHandler.checkRevoke(activityId, Status.Refuse);

        logger.info("测试结果(拒绝To撤审):{}", JSON.toJSONString(result));
        logger.info("活动信息:{} 状态:{}", JSON.toJSONString(ActivityService.queryActivityInfo(activityId)), JSON.toJSONString(ActivityService.queryActivityInfo(activityId).getStatus()));
    }
}

测试结果:

19:49:48.755 [main] INFO  ApiTest - 测试结果(拒绝To撤审):{"code":"0000","info":"拒绝后返回编辑状态"}
19:49:48.768 [main] INFO  ApiTest - 活动信息:{"activityId":"100001","activityName":"测试活动","beginTime":1649591388759,"endTime":1649591388759,"status":"Editing"} 状态:"Editing"

参考资料

《重学Java设计模式》

《设计模式》

目录
相关文章
|
2月前
|
设计模式 消息中间件 搜索推荐
Java 设计模式——观察者模式:从优衣库不使用新疆棉事件看系统的动态响应
【11月更文挑战第17天】观察者模式是一种行为设计模式,定义了一对多的依赖关系,使多个观察者对象能直接监听并响应某一主题对象的状态变化。本文介绍了观察者模式的基本概念、商业系统中的应用实例,如优衣库事件中各相关方的动态响应,以及模式的优势和实际系统设计中的应用建议,包括事件驱动架构和消息队列的使用。
|
2月前
|
设计模式 Java 数据库连接
Java编程中的设计模式:单例模式的深度剖析
【10月更文挑战第41天】本文深入探讨了Java中广泛使用的单例设计模式,旨在通过简明扼要的语言和实际示例,帮助读者理解其核心原理和应用。文章将介绍单例模式的重要性、实现方式以及在实际应用中如何优雅地处理多线程问题。
48 4
|
3月前
|
设计模式 Java 程序员
[Java]23种设计模式
本文介绍了设计模式的概念及其七大原则,强调了设计模式在提高代码重用性、可读性、可扩展性和可靠性方面的作用。文章还简要概述了23种设计模式,并提供了进一步学习的资源链接。
67 0
[Java]23种设计模式
|
2月前
|
设计模式 JavaScript Java
Java设计模式:建造者模式详解
建造者模式是一种创建型设计模式,通过将复杂对象的构建过程与表示分离,使得相同的构建过程可以创建不同的表示。本文详细介绍了建造者模式的原理、背景、应用场景及实际Demo,帮助读者更好地理解和应用这一模式。
|
3月前
|
设计模式 监控 算法
Java设计模式梳理:行为型模式(策略,观察者等)
本文详细介绍了Java设计模式中的行为型模式,包括策略模式、观察者模式、责任链模式、模板方法模式和状态模式。通过具体示例代码,深入浅出地讲解了每种模式的应用场景与实现方式。例如,策略模式通过定义一系列算法让客户端在运行时选择所需算法;观察者模式则让多个观察者对象同时监听某一个主题对象,实现松耦合的消息传递机制。此外,还探讨了这些模式与实际开发中的联系,帮助读者更好地理解和应用设计模式,提升代码质量。
Java设计模式梳理:行为型模式(策略,观察者等)
|
4月前
|
存储 设计模式 安全
Java设计模式-备忘录模式(23)
Java设计模式-备忘录模式(23)
|
4月前
|
设计模式 存储 算法
Java设计模式-命令模式(16)
Java设计模式-命令模式(16)
|
4月前
|
设计模式 存储 缓存
Java设计模式 - 解释器模式(24)
Java设计模式 - 解释器模式(24)
|
4月前
|
设计模式 安全 Java
Java设计模式-迭代器模式(21)
Java设计模式-迭代器模式(21)
|
4月前
|
设计模式 缓存 监控
Java设计模式-责任链模式(17)
Java设计模式-责任链模式(17)