Java设计模式:四、行为型模式-07:状态模式

news2024/12/23 4:56:03

文章目录

  • 一、定义:状态模式
  • 二、模拟场景:状态模式
    • 2.1 状态模式
    • 2.2 引入依赖
    • 2.3 工程结构
    • 2.4 模拟审核状态流转
      • 2.4.1 活动状态枚举
      • 2.4.2 活动信息类
      • 2.4.3 活动服务接口
      • 2.4.4 返回结果类
  • 三、违背方案:状态模式
    • 3.0 引入依赖
    • 3.1 工程结构
    • 3.2 活动执行状态变更控制层
    • 3.3 单元测试
  • 四、改善代码:状态模式
    • 4.0 引入依赖
    • 4.1 工程结构
    • 4.2 状态模式结构图
    • 4.3 活动状态变更流程
      • 4.3.1 定义状态抽象类
      • 4.3.2 待审核状态流转实现
      • 4.3.3 活动关闭状态流转实现
      • 4.3.4 活动中状态流转实现
      • 4.3.5 编辑中状态流转实现
      • 4.3.6 活动开启状态流转实现
      • 4.3.7 审核通过状态流转实现
      • 4.3.8 审核拒绝状态流转实现
      • 4.3.9 活动执行者
    • 4.4 单元测试
      • 4.4.1 编辑中到提审活动测试验证
      • 4.4.2 编辑中到开启活动测试验证
      • 4.4.3 审批拒绝到活动中测试验证
      • 4.4.4 审批拒绝到撤审测试验证
  • 五、总结:状态模式

一、定义:状态模式

请添加图片描述

  • **状态模式:**是一个行为下的多种状态变更。
    • 比如我们常见的一个网站的页面,在你登录与不登录下展示的内容是略有差异的 (不登录不能展示个人信息)
    • 这种 登录不登录 就是我们通过改变 状态,而让整个行为发生了变化。
  • 状态模式的使用场景。例如:磁带放音机。
    • 它的上面是一排按钮,当放入磁带后,通过上面的按钮就可以让放音机播放磁带上的内容。
    • 而且有些按钮是互斥的,当在某个状态下才可以按另外的按钮(这在设计模式里也是一个关键的点 )。

二、模拟场景:状态模式

2.1 状态模式

请添加图片描述

  • 模拟营销活动审核状态流转场景(一个活动的上线是多个层级审核上线的)。
  • 在上图中可以看到我们的流程节点中包括了各个状态到下一个状态扭转的关联条件。
    • 比如:审核通过才能到活动中,而不能从编辑中直接到活动中,而这些状态的转变就是我们要完成的场景处理。
  • 我们都开发过类似的业务场景,需要对活动或者一些配置需要审核后才能对外发布,而这个审核的过程往往会随着系统的重要程序而设立多级控制,来保证一个活动可以安全上线,避免造成资损。
  • 当然有时候会用到一些审批流的过程配置,也是非常方便开发类似的流程的,也可以在配置中设定某个节点的审批人员。
  • 在本案例中们我主要是模拟学习对一个活动的多个状态节点的审核控制。

2.2 引入依赖

pom.xml

<dependencies>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>
    <!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>1.2.62</version>
    </dependency>
    <!-- LOGGING begin -->
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>1.7.5</version>
    </dependency>
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>jcl-over-slf4j</artifactId>
        <version>1.7.5</version>
    </dependency>
    <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-classic</artifactId>
        <version>1.0.9</version>
        <exclusions>
            <exclusion>
                <artifactId>slf4j-api</artifactId>
                <groupId>org.slf4j</groupId>
            </exclusion>
        </exclusions>
    </dependency>
</dependencies>

2.3 工程结构

design-20.0-0
|——src
	|——main
		|--java
			|--com.lino.design
				|-ActicityInfo.java
				|-ActivityService.java
				|-Result.java
				|-Status.java

2.4 模拟审核状态流转

2.4.1 活动状态枚举

Status.java

package com.lino.design;

/**
 * @description: 状态枚举
 */
public enum Status {

    /**
     * 1.创建编辑,2.待审核,3.审核通过(任务扫描成活动中),
     * 4.审核拒绝(可以撤审到编辑状态),5.活动中,
     * 6.活动关闭,7.活动开启(任务扫描成活动中)
     */
    Editing, Check, Pass, Refuse, Doing, Close, Open
}

2.4.2 活动信息类

ActicityInfo.java

package com.lino.design;

import java.util.Date;
import java.util.Map;

/**
 * @description: 活动类
 */
public class ActicityInfo {
    /**
     * 活动ID
     */
    private String acticityId;
    /**
     * 活动名称
     */
    private String acticityName;
    /**
     * 活动状态
     */
    private Enum<Status> status;
    /**
     * 开始时间
     */
    private Date beginTime;
    /**
     * 结束时间
     */
    private Date endTime;

    public String getActicityId() {
        return acticityId;
    }

    public void setActicityId(String acticityId) {
        this.acticityId = acticityId;
    }

    public String getActicityName() {
        return acticityName;
    }

    public void setActicityName(String acticityName) {
        this.acticityName = acticityName;
    }

    public Enum<Status> getStatus() {
        return status;
    }

    public void setStatus(Enum<Status> status) {
        this.status = status;
    }

    public Date getBeginTime() {
        return beginTime;
    }

    public void setBeginTime(Date beginTime) {
        this.beginTime = beginTime;
    }

    public Date getEndTime() {
        return endTime;
    }

    public void setEndTime(Date endTime) {
        this.endTime = endTime;
    }
}
  • 基本的活动信息:活动ID、活动名称、活动状态、开始时间、结束时间

2.4.3 活动服务接口

ActivityService.java

package com.lino.design;

import java.util.Date;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @description: 活动服务类
 */
public class ActivityService {

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

    /**
     * 初始化活动
     *
     * @param activityId 活动ID
     * @param status     活动状态
     */
    public static void init(String activityId, Enum<Status> status) {
        // 模拟查询活动信息
        ActicityInfo acticityInfo = new ActicityInfo();
        acticityInfo.setActicityId(activityId);
        acticityInfo.setActicityName("早起学习打卡领奖活动");
        acticityInfo.setStatus(status);
        acticityInfo.setBeginTime(new Date());
        acticityInfo.setEndTime(new Date());
        statusMap.put(activityId, status);
    }

    /**
     * 查询活动信息
     *
     * @param activityId 活动ID
     * @return 活动信息
     */
    public static ActicityInfo queryActivityInfo(String activityId) {
        // 模拟查询活动信息
        ActicityInfo acticityInfo = new ActicityInfo();
        acticityInfo.setActicityId(activityId);
        acticityInfo.setActicityName("早起学习打卡领奖活动");
        acticityInfo.setStatus(statusMap.get(activityId));
        acticityInfo.setBeginTime(new Date());
        acticityInfo.setEndTime(new Date());
        return acticityInfo;
    }

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

    /**
     * 执行状态变更
     *
     * @param activityId   活动ID
     * @param beforeStatus 变更前状态
     * @param afterStatus  变更后状态
     */
    public static synchronized void execStatus(String activityId, Enum<Status> beforeStatus, Enum<Status> afterStatus) {
        if (!beforeStatus.equals(statusMap.get(activityId))) {
            return;
        }
        statusMap.put(activityId, afterStatus);
    }
}
  • 在这个静态类中提供了活动的查询和状态变更接口。
    • queryActivityInfo :查询活动信息。
    • queryActivityStatus :查询活动状态。
    • execStatus:执行状态变更。
  • 同时使用 Map 的结构来记录活动ID 和状态变化信息,另外还有 init 方法来初始化活动数据。
    • 实际的开发中这类信息基本都是从 数据库 或者 Redis 中获取。

2.4.4 返回结果类

Result.java

package com.lino.design;

/**
 * @description: 结果类
 */
public class Result {
    /**
     * 编码
     */
    private String code;
    /**
     * 描述
     */
    private String info;

    public Result(String code, String info) {
        this.code = code;
        this.info = info;
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public String getInfo() {
        return info;
    }

    public void setInfo(String info) {
        this.info = info;
    }
}

三、违背方案:状态模式

对于这样各种状态的变更,最让我们直接想到的就是使用 ifelse 进行判断处理。
每一个状态可以流转到下一个什么状态,都可以使用嵌套的 if 实现。

3.0 引入依赖

<dependencies>
    <dependency>
        <groupId>com.lino</groupId>
        <artifactId>design-20.0-0</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>
</dependencies>

3.1 工程结构

design-20.0-1
|——src
	|——main
		|--java
			|--com.lino.design
				|-ActivityExecStatusController.java
		|--test
			|--com.lino.design.test
				|-ApiTest.java

3.2 活动执行状态变更控制层

ActivityExecStatusController.java

package com.lino.design;

/**
 * @description: 活动状态变更控制层
 */
public class ActivityExecStatusController {

    /**
     * 活动状态变更
     * 1. 编辑中 -> 提审、关闭
     * 2. 审核通过 -> 拒绝、关闭、活动中
     * 3. 审核拒绝 -> 撤审、关闭
     * 4. 活动中 -> 关闭
     * 5. 活动关闭 -> 开启
     * 6. 活动开启 -> 关闭
     *
     * @param activityId   活动ID
     * @param beforeStatus 变更前状态
     * @param afterStatus  变更后状态
     * @return 返回结果
     */
    public Result execStatus(String activityId, Enum<Status> beforeStatus, Enum<Status> afterStatus) {

        // 1. 编辑中 -> 提审、关闭
        if (Status.Editing.equals(beforeStatus)) {
            if (Status.Check.equals(afterStatus) || Status.Close.equals(afterStatus)) {
                ActivityService.execStatus(activityId, beforeStatus, afterStatus);
                return new Result("0000", "变更状态成功");
            } else {
                return new Result("0001", "变更状态拒绝");
            }
        }

        // 2. 审核通过 -> 拒绝、关闭、活动中
        if (Status.Pass.equals(beforeStatus)) {
            if (Status.Refuse.equals(afterStatus) || Status.Doing.equals(afterStatus) || Status.Close.equals(afterStatus)) {
                ActivityService.execStatus(activityId, beforeStatus, afterStatus);
                return new Result("0000", "变更状态成功");
            } else {
                return new Result("0001", "变更状态拒绝");
            }
        }

        // 3. 审核拒绝 -> 撤审、关闭
        if (Status.Refuse.equals(beforeStatus)) {
            if (Status.Editing.equals(afterStatus) || Status.Close.equals(afterStatus)) {
                ActivityService.execStatus(activityId, beforeStatus, afterStatus);
                return new Result("0000", "变更状态成功");
            } else {
                return new Result("0001", "变更状态拒绝");
            }
        }

        // 4. 活动中 -> 关闭
        if (Status.Doing.equals(beforeStatus)) {
            if (Status.Close.equals(afterStatus)) {
                ActivityService.execStatus(activityId, beforeStatus, afterStatus);
                return new Result("0000", "变更状态成功");
            } else {
                return new Result("0001", "变更状态拒绝");
            }
        }

        // 5. 活动关闭 -> 开启
        if (Status.Close.equals(beforeStatus)) {
            if (Status.Open.equals(afterStatus)) {
                ActivityService.execStatus(activityId, beforeStatus, afterStatus);
                return new Result("0000", "变更状态成功");
            } else {
                return new Result("0001", "变更状态拒绝");
            }
        }

        // 6. 活动开启 -> 关闭
        if (Status.Open.equals(beforeStatus)) {
            if (Status.Close.equals(afterStatus)) {
                ActivityService.execStatus(activityId, beforeStatus, afterStatus);
                return new Result("0000", "变更状态成功");
            } else {
                return new Result("0001", "变更状态拒绝");
            }
        }

        return new Result("0001", "非可处理的活动状态变更");
    }
}
  • 从这个代码实现的结构看,从上到下是一整篇的 if-else
  • 这样的面向过程式开发方式,对于不需要改动代码,也不需要二次迭代的,还是可以使用的(但基本不可能不迭代)。
  • 而且随着状态和需求变化,会越来越难为维护,后面的人也不好看懂并且很容易填充其他的流程进去。

3.3 单元测试

ApiTest.java

package com.lino.design.test;

import com.alibaba.fastjson.JSON;
import com.lino.design.ActivityExecStatusController;
import com.lino.design.ActivityService;
import com.lino.design.Result;
import com.lino.design.Status;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @description: 单元测试
 */
public class ApiTest {

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

    @Test
    public void test() {
        // 初始化数据
        String activityId = "100001";
        ActivityService.init(activityId, Status.Editing);

        ActivityExecStatusController activityExecStatusController = new ActivityExecStatusController();
        Result resultRefuse = activityExecStatusController.execStatus(activityId, Status.Editing, Status.Refuse);
        logger.info("测试结果(编辑中To审核拒绝):{}", JSON.toJSONString(resultRefuse));

        Result resultCheck = activityExecStatusController.execStatus(activityId, Status.Editing, Status.Check);
        logger.info("测试结果(编辑中To提交审核):{}", JSON.toJSONString(resultCheck));
    }
}
  • 这个测试主要包括两个功能的验证,一个是从 编辑中审核拒绝 ,另外一个是从 编辑中提交审核
  • 因为从我们的场景流程中可以看到,编辑中 的活动是不能直接到 审核拒绝 ,这中间还需要 提审

测试结果

17:03:34.528 [main] INFO  com.lino.design.test.ApiTest - 测试结果(编辑中To审核拒绝){"code":"0001","info":"变更状态拒绝"}
17:03:34.534 [main] INFO  com.lino.design.test.ApiTest - 测试结果(编辑中To提交审核){"code":"0000","info":"变更状态成功"}

四、改善代码:状态模式

💡 重构的重点往往是处理掉 if-else ,而想处理掉 if-else 基本离不开接口与抽象类,另外还需要重新改造代码结构。

4.0 引入依赖

<dependencies>
    <dependency>
        <groupId>com.lino</groupId>
        <artifactId>design-20.0-0</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>
</dependencies>

4.1 工程结构

design-19.0-2
|——src
	|——main
		|--java
			|--com.lino.design
				|--impl
				|		|--CheckState.java
				|		|--CloseState.java
				|		|--DoingState.java
				|		|--EditingState.java
				|		|--OpenState.java
				|		|--PassState.java
				|		|--RefuseState.java
				|-State.java
				|-StateHandler.java
		|--test
			|--com.lino.design.test
				|-ApiTest.java

4.2 状态模式结构图

请添加图片描述

  • 以上是状态模式的整个工程结构模型,State 是一个抽象类,定义了各种操作接口(提审、审核、拒审等)。
  • 右侧的不同颜色状态与我们场景模拟中的颜色保持一致,是各种状态流程流转的实现操作。
    • 这里的实现有一个关键点就是每一种状态到下一个状态,都分配到各个实现方法中控制,也就不需要 if 语言进行判断了。
  • 最后是 StateHandler对状态流程的统一处理,里面提供 Map 结构的各项服务接口调用,也就避免了使用 if 判断各项状态转变的流程。

4.3 活动状态变更流程

4.3.1 定义状态抽象类

State.java

package com.lino.design;

/**
 * @description: 活动状态抽象类
 */
public abstract class State {

    /**
     * 活动提审
     *
     * @param activityId    活动ID
     * @param currentStatus 当前状态
     * @return 执行结果
     */
    public abstract Result arraignment(String activityId, Enum<Status> currentStatus);

    /**
     * 审核通过
     *
     * @param activityId    活动ID
     * @param currentStatus 当前状态
     * @return 执行结果
     */
    public abstract Result checkPass(String activityId, Enum<Status> currentStatus);

    /**
     * 审核拒绝
     *
     * @param activityId    活动ID
     * @param currentStatus 当前状态
     * @return 执行结果
     */
    public abstract Result checkRefuse(String activityId, Enum<Status> currentStatus);

    /**
     * 撤审撤销
     *
     * @param activityId    活动ID
     * @param currentStatus 当前状态
     * @return 执行结果
     */
    public abstract Result checkRevoke(String activityId, Enum<Status> currentStatus);

    /**
     * 活动关闭
     *
     * @param activityId    活动ID
     * @param currentStatus 当前状态
     * @return 执行结果
     */
    public abstract Result close(String activityId, Enum<Status> currentStatus);

    /**
     * 活动开启
     *
     * @param activityId    活动ID
     * @param currentStatus 当前状态
     * @return 执行结果
     */
    public abstract Result open(String activityId, Enum<Status> currentStatus);

    /**
     * 活动执行
     *
     * @param activityId    活动ID
     * @param currentStatus 当前状态
     * @return 执行结果
     */
    public abstract Result doing(String activityId, Enum<Status> currentStatus);
}
  • 整个接口中提供了各项状态流转服务的接口,例如:活动提审审核通过审核拒绝撤审撤销活动关闭活动开启 、**活动执行
  • 在这些方法中所有的入参都是一样的,activityId (活动ID)currentStatus (当前状态),只有他们的具体实现是不同的。

4.3.2 待审核状态流转实现

CheckState.java

package com.lino.design.impl;

import com.lino.design.ActivityService;
import com.lino.design.Result;
import com.lino.design.State;
import com.lino.design.Status;

/**
 * @description: 待审核
 */
public class CheckState extends State {
    @Override
    public Result arraignment(String activityId, Enum<Status> currentStatus) {
        return new Result("0001", "待审核状态不可重复提审");
    }

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

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

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

    @Override
    public Result close(String activityId, Enum<Status> currentStatus) {
        ActivityService.execStatus(activityId, currentStatus, Status.Close);
        return new Result("0000", "活动审核关闭完成");
    }

    @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", "待审核活动不可执行活动中变更");
    }
}

4.3.3 活动关闭状态流转实现

CloseState.java

package com.lino.design.impl;

import com.lino.design.ActivityService;
import com.lino.design.Result;
import com.lino.design.State;
import com.lino.design.Status;

/**
 * @description: 活动关闭
 */
public class CloseState extends State {
    @Override
    public Result arraignment(String activityId, Enum<Status> currentStatus) {
        return new Result("0001", "活动关闭不可提审");
    }

    @Override
    public Result checkPass(String activityId, Enum<Status> currentStatus) {
        return new Result("0001", "活动关闭不可审核通过");
    }

    @Override
    public Result checkRefuse(String activityId, Enum<Status> currentStatus) {
        return new Result("0001", "活动关闭不可审核拒绝");
    }

    @Override
    public Result checkRevoke(String activityId, Enum<Status> currentStatus) {
        return new Result("0001", "活动关闭不可撤销审核");
    }

    @Override
    public Result close(String activityId, Enum<Status> currentStatus) {
        return new Result("0001", "活动关闭不可重复关闭");
    }

    @Override
    public Result open(String activityId, Enum<Status> currentStatus) {
        ActivityService.execStatus(activityId, currentStatus, Status.Open);
        return new Result("0000", "活动开启完成");
    }

    @Override
    public Result doing(String activityId, Enum<Status> currentStatus) {
        return new Result("0001", "活动关闭不可变更活动中");
    }
}

4.3.4 活动中状态流转实现

DoingState.java

package com.lino.design.impl;

import com.lino.design.ActivityService;
import com.lino.design.Result;
import com.lino.design.State;
import com.lino.design.Status;

/**
 * @description: 活动中
 */
public class DoingState extends State {
    @Override
    public Result arraignment(String activityId, Enum<Status> currentStatus) {
        return new Result("0001", "活动中不可提审");
    }

    @Override
    public Result checkPass(String activityId, Enum<Status> currentStatus) {
        return new Result("0001", "活动中不可审核通过");
    }

    @Override
    public Result checkRefuse(String activityId, Enum<Status> currentStatus) {
        return new Result("0001", "活动中不可审核拒绝");
    }

    @Override
    public Result checkRevoke(String activityId, Enum<Status> currentStatus) {
        return new Result("0001", "活动中不可撤销审核");
    }

    @Override
    public Result close(String activityId, Enum<Status> currentStatus) {
        ActivityService.execStatus(activityId, currentStatus, Status.Close);
        return new Result("0000", "活动关闭完成");

    }

    @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", "活动中不可重复执行");
    }
}

4.3.5 编辑中状态流转实现

EditingState.java

package com.lino.design.impl;

import com.lino.design.ActivityService;
import com.lino.design.Result;
import com.lino.design.State;
import com.lino.design.Status;

/**
 * @description: 编辑中
 */
public class EditingState extends State {
    @Override
    public Result arraignment(String activityId, Enum<Status> currentStatus) {
        ActivityService.execStatus(activityId, currentStatus, Status.Check);
        return new Result("0000", "活动提审成功");
    }

    @Override
    public Result checkPass(String activityId, Enum<Status> currentStatus) {
        return new Result("0001", "编辑中不可审核通过");
    }

    @Override
    public Result checkRefuse(String activityId, Enum<Status> currentStatus) {
        return new Result("0001", "编辑中不可审核拒绝");
    }

    @Override
    public Result checkRevoke(String activityId, Enum<Status> currentStatus) {
        return new Result("0001", "编辑中不可撤销审核");
    }

    @Override
    public Result close(String activityId, Enum<Status> currentStatus) {
        ActivityService.execStatus(activityId, currentStatus, Status.Close);
        return new Result("0000", "活动关闭完成");

    }

    @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", "编辑活动中不可执行活动中变更");
    }
}

4.3.6 活动开启状态流转实现

OpenState.java

package com.lino.design.impl;

import com.lino.design.ActivityService;
import com.lino.design.Result;
import com.lino.design.State;
import com.lino.design.Status;

/**
 * @description: 活动开启
 */
public class OpenState extends State {
    @Override
    public Result arraignment(String activityId, Enum<Status> currentStatus) {
        return new Result("0001", "活动开启不可提审");
    }

    @Override
    public Result checkPass(String activityId, Enum<Status> currentStatus) {
        return new Result("0001", "活动开启不可审核通过");
    }

    @Override
    public Result checkRefuse(String activityId, Enum<Status> currentStatus) {
        return new Result("0001", "活动开启不可审核拒绝");
    }

    @Override
    public Result checkRevoke(String activityId, Enum<Status> currentStatus) {
        return new Result("0001", "活动开启不可撤销审核");
    }

    @Override
    public Result close(String activityId, Enum<Status> currentStatus) {
        ActivityService.execStatus(activityId, currentStatus, Status.Close);
        return new Result("0000", "活动关闭完成");

    }

    @Override
    public Result open(String activityId, Enum<Status> currentStatus) {
        return new Result("0001", "活动不可重复开启");
    }

    @Override
    public Result doing(String activityId, Enum<Status> currentStatus) {
        ActivityService.execStatus(activityId, currentStatus, Status.Doing);
        return new Result("0000", "活动变更活动中完成");
    }
}

4.3.7 审核通过状态流转实现

PassState.java

package com.lino.design.impl;

import com.lino.design.ActivityService;
import com.lino.design.Result;
import com.lino.design.State;
import com.lino.design.Status;

/**
 * @description: 审核通过
 */
public class PassState extends State {
    @Override
    public Result arraignment(String activityId, Enum<Status> currentStatus) {
        return new Result("0001", "已审核状态不可重复提审");
    }

    @Override
    public Result checkPass(String activityId, Enum<Status> currentStatus) {
        return new Result("0001", "已审核状态不可重复审核");
    }

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

    @Override
    public Result checkRevoke(String activityId, Enum<Status> currentStatus) {
        return new Result("0001", "审核通过不可撤销(可先拒绝审核)");
    }

    @Override
    public Result close(String activityId, Enum<Status> currentStatus) {
        ActivityService.execStatus(activityId, currentStatus, Status.Close);
        return new Result("0000", "活动关闭完成");

    }

    @Override
    public Result open(String activityId, Enum<Status> currentStatus) {
        return new Result("0001", "非关闭活动不可开启");
    }

    @Override
    public Result doing(String activityId, Enum<Status> currentStatus) {
        ActivityService.execStatus(activityId, currentStatus, Status.Doing);
        return new Result("0000", "活动变更活动中完成");
    }
}

4.3.8 审核拒绝状态流转实现

RefuseState.java

package com.lino.design.impl;

import com.lino.design.ActivityService;
import com.lino.design.Result;
import com.lino.design.State;
import com.lino.design.Status;

/**
 * @description: 审核拒绝
 */
public class RefuseState extends State {
    @Override
    public Result arraignment(String activityId, Enum<Status> currentStatus) {
        return new Result("0001", "已审核状态不可重复提审");
    }

    @Override
    public Result checkPass(String activityId, Enum<Status> currentStatus) {
        return new Result("0001", "已审核状态不可重复审核");
    }

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

    @Override
    public Result checkRevoke(String activityId, Enum<Status> currentStatus) {
        ActivityService.execStatus(activityId, currentStatus, Status.Editing);
        return new Result("0000", "撤销审核完成");
    }

    @Override
    public Result close(String activityId, Enum<Status> currentStatus) {
        ActivityService.execStatus(activityId, currentStatus, Status.Close);
        return new Result("0000", "活动审核关闭完成");

    }

    @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", "审核拒绝不可执行活动为进行中");
    }
}
  • 这里提供了七个具体实现类。待审核活动关闭活动中编辑中活动开启审核通过审核拒绝
  • 在每个实现类中,每个方法对于不同的类中有不同的实现,也就是不同状态下一步流转操作已经可以在每一个方法中具体控制了。

4.3.9 活动执行者

StateHandler.java

package com.lino.design;

import com.lino.design.impl.*;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @description: 活动执行者
 */
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.Editing, new EditingState());
        // 已开启
        stateMap.put(Status.Open, new OpenState());
        // 审核通过
        stateMap.put(Status.Pass, new PassState());
        // 审核拒绝
        stateMap.put(Status.Refuse, new RefuseState());
    }

    /**
     * 活动提审
     *
     * @param activityId    活动ID
     * @param currentStatus 当前状态
     * @return 执行结果
     */
    public Result arraignment(String activityId, Enum<Status> currentStatus) {
        return stateMap.get(currentStatus).arraignment(activityId, currentStatus);
    }

    /**
     * 审核通过
     *
     * @param activityId    活动ID
     * @param currentStatus 当前状态
     * @return 执行结果
     */
    public Result checkPass(String activityId, Enum<Status> currentStatus) {
        return stateMap.get(currentStatus).checkPass(activityId, currentStatus);
    }

    /**
     * 审核拒绝
     *
     * @param activityId    活动ID
     * @param currentStatus 当前状态
     * @return 执行结果
     */
    public Result checkRefuse(String activityId, Enum<Status> currentStatus) {
        return stateMap.get(currentStatus).checkRefuse(activityId, currentStatus);
    }

    /**
     * 撤审撤销
     *
     * @param activityId    活动ID
     * @param currentStatus 当前状态
     * @return 执行结果
     */
    public Result checkRevoke(String activityId, Enum<Status> currentStatus) {
        return stateMap.get(currentStatus).checkRevoke(activityId, currentStatus);
    }

    /**
     * 活动关闭
     *
     * @param activityId    活动ID
     * @param currentStatus 当前状态
     * @return 执行结果
     */
    public Result close(String activityId, Enum<Status> currentStatus) {
        return stateMap.get(currentStatus).close(activityId, currentStatus);
    }

    /**
     * 活动开启
     *
     * @param activityId    活动ID
     * @param currentStatus 当前状态
     * @return 执行结果
     */
    public Result open(String activityId, Enum<Status> currentStatus) {
        return stateMap.get(currentStatus).open(activityId, currentStatus);
    }

    /**
     * 活动执行
     *
     * @param activityId    活动ID
     * @param currentStatus 当前状态
     * @return 执行结果
     */
    public Result doing(String activityId, Enum<Status> currentStatus) {
        return stateMap.get(currentStatus).doing(activityId, currentStatus);
    }
}
  • 这是对状态服务的统一控制中心,可以看到子啊构造函数中提供了所有状态和实现的具体关联,放到 Map 数据结构中。
  • 同时提供了不同名称的接口操作类,让外部调用方可以更加容易的使用此项功能接口,而不需要像在之前还得传两个状态来判断。

4.4 单元测试

4.4.1 编辑中到提审活动测试验证

ApiTest.java

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

    StateHandler stateHandler = new StateHandler();
    Result result = stateHandler.arraignment(activityId, Status.Editing);

    logger.info("测试结果(编辑中To提审活动):{}", JSON.toJSONString(result));
    logger.info("活动信息:{} 状态:{}", JSON.toJSONString(ActivityService.queryActivityInfo(activityId)), JSON.toJSONString(ActivityService.queryActivityStatus(activityId)));
}

测试结果

20:48:10.752 [main] INFO  com.lino.design.test.ApiTest - 测试结果(编辑中To提审活动){"code":"0000","info":"活动提审成功"}
20:48:10.788 [main] INFO  com.lino.design.test.ApiTest - 活动信息:{"activityId":"100001","activityName":"早起学习打卡领奖活动","beginTime":1675860490764,"endTime":1675860490764,"status":"Check"} 状态:"Check"
  • 从编辑中到提审活动到状态流转,测试验证结果显示正常。

4.4.2 编辑中到开启活动测试验证

ApiTest.java

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

    StateHandler stateHandler = new StateHandler();
    Result result = stateHandler.open(activityId, Status.Editing);

    logger.info("测试结果(编辑中To开启活动):{}", JSON.toJSONString(result));
    logger.info("活动信息:{} 状态:{}", JSON.toJSONString(ActivityService.queryActivityInfo(activityId)), JSON.toJSONString(ActivityService.queryActivityStatus(activityId)));
}

测试结果

20:48:57.997 [main] INFO  com.lino.design.test.ApiTest - 测试结果(编辑中To开启活动){"code":"0001","info":"非关闭活动不可开启"}
20:48:58.041 [main] INFO  com.lino.design.test.ApiTest - 活动信息:{"activityId":"100001","activityName":"早起学习打卡领奖活动","beginTime":1675860538008,"endTime":1675860538008,"status":"Editing"} 状态:"Editing"
  • 从编辑中到开启活动到状态流转可以看到,测试结果是拒绝- 非关闭活动不可开启
  • 按照流程流转结果,预期结果正常,不能从编辑中直接到活动开启,需要经过审批阶段。

4.4.3 审批拒绝到活动中测试验证

ApiTest.java

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

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

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

测试结果

20:49:51.974 [main] INFO  com.lino.design.test.ApiTest - 测试结果(拒绝To活动中){"code":"0001","info":"审核拒绝不可执行活动为进行中"}
20:49:52.015 [main] INFO  com.lino.design.test.ApiTest - 活动信息:{"activityId":"100001","activityName":"早起学习打卡领奖活动","beginTime":1675860591979,"endTime":1675860591979,"status":"Refuse"} 状态:"Refuse"
  • 从审批拒绝到活动中到状态流转可以看到,同样是拒绝- 审批拒绝不可执行活动中 为进行中,满足预期。

4.4.4 审批拒绝到撤审测试验证

ApiTest.java

@Test
public void test_Editing2Revoke() {
    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.queryActivityStatus(activityId)));
}

测试结果

20:50:39.921 [main] INFO  com.lino.design.test.ApiTest - 测试结果(拒绝To撤审){"code":"0000","info":"撤销审核完成"}
20:50:39.960 [main] INFO  com.lino.design.test.ApiTest - 活动信息:{"activityId":"100001","activityName":"早起学习打卡领奖活动","beginTime":1675860639937,"endTime":1675860639937,"status":"Editing"} 状态:"Editing"
  • 从审批拒绝到撤审到状态流转可以看到,按照状态到流转约定,审批拒绝后可以撤审,测试结果满足预期。

💡 综上,以上四个测试类分别模拟了不同状态之间到有效流转和拒绝流转,不同的状态服务处理不同的服务内容。

五、总结:状态模式

  • 从以上两种方式对一个需求的实现对比可以看到,在使用设计模式处理后,已经没有了 if-else ,代码的结构也更加清晰,易于扩展。
  • 在实现结构的编码方式上,可以看到不再是面向过程的编程,而是面向对象的编程。
  • 状态模式的优点:
    • 状态模式满足了单一职责和开闭原则。
    • 当只有满足这种结构时,才会发现代码的扩展是容易的,也就是增加或修改功能不会影响整体。
  • 状态模式的缺点:
    • 如果状态和各项流转较多,就会产生较多的实现类。因此,可能会给代码的实现增加时间成本。
    • 因为如果遇到这种场景可以按需评估投入回报率,主要在于是否会经常修改,是否可以做成组件化,抽离业务功能与非业务功能。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/957499.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

JVM的故事——垃圾收集器

垃圾收集器 文章目录 垃圾收集器一、serial收集器二、parnew收集器三、parallel scavenge收集器四、serial old收集器五、parallel old收集器六、CMS收集器七、Garbage First收集器八、收集器的权衡 一、serial收集器 新生代收集器&#xff0c;最基础的收集器&#xff0c;单线…

C#备份数据库文件

c#备份数据库文件完整代码 sqlServer 存储过程&#xff1a; USE [PSIDBase] GO /****** Object: StoredProcedure [dbo].[sp_BackupDB] Script Date: 2023/8/31 16:49:02 ******/ SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GOALTER procedure [dbo].[sp_BackupDB]…

【Unity每日一记】WheelColider组件汽车游戏的关键

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a;uni…

2023年腾讯云优惠券(代金券)领取方法整理汇总

腾讯云优惠券是腾讯云为了吸引用户而推出的一种优惠凭证&#xff0c;领券之后新购、续费、升级腾讯云的相关产品可以享受优惠&#xff0c;从而节省一点的费用&#xff0c;下面给大家分享腾讯云优惠券领取的几种方法。 一、腾讯云官网领券页面领取 腾讯云官网经常推出各种优惠活…

C#,数值计算——Midinf的计算方法与源程序

1 文本格式 using System; namespace Legalsoft.Truffer { public class Midinf : Midpnt { public new double func(double x) { return funk.funk(1.0 / x) / (x * x); } public Midinf(UniVarRealValueFun funcc, double aa,…

内存四区(个人学习笔记黑马学习)

1、内存分区模型 C程序在执行时&#xff0c;将内存大方向划分为4个区域&#xff1a; 代码区:存放函数体的二进制代码&#xff0c;由操作系统进行管理的全局区:存放全局变量和静态变量以及常量栈区:编译器自动分配释放,存放函数的参数值,局部变量等 堆区:由程序员分配和释放,若程…

SpringBoot整合Freemaker结合Vue实现页面填写一键自动生成Redis的配置文件

&#x1f9d1;‍&#x1f4bb;作者名称&#xff1a;DaenCode &#x1f3a4;作者简介&#xff1a;啥技术都喜欢捣鼓捣鼓&#xff0c;喜欢分享技术、经验、生活。 &#x1f60e;人生感悟&#xff1a;尝尽人生百味&#xff0c;方知世间冷暖。 &#x1f4d6;所属专栏&#xff1a;Sp…

DCMM数据能力成熟度评估模型--学习笔记

DCMM数据能力成熟度评估模型--学习笔记 1、DCMM简介、结构组成和成熟度评估等级划分1.1 DCMM简介1.2 DCMM结构组成1.3 DCMM关键过程域1.3.1、数据战略&#xff08;指导方针&#xff09;1.3.2、数据治理 &#xff08;机制保障&#xff09;1.3.3、数据架构 (施工图纸)1.3.4、数据…

WebRTC-Streamer交叉编译

WebRTC-Streamer交叉编译 flyfish 文章目录 WebRTC-Streamer交叉编译零、前言一、提前准备工作1 安装需要的工具2 可选的交叉编译工具3 默认执行python是python34 获取源码5 使用其他版本的方法 二、非交叉编译编译1 在 src目录执行 安装所需的依赖2 执行命令 三、 交叉编译1 …

【GAMES202】Real-Time Global Illumination(in 3D)—实时全局光照(3D空间)

一、SH for Glossy transport 1.Diffuse PRT回顾 上篇我们介绍了PRT&#xff0c;并以Diffuse的BRDF作为例子分析了预计算的部分&#xff0c;包括Lighting和Light transport&#xff0c;如上图所示。 包括我们还提到了SH&#xff0c;可以用SH的有限阶近似拟合球面函数&#xff…

【进阶篇】MySQL的SQL解析原理详解

文章目录 0. 前言1. SQL解析过程1. 词法分析2. 语法分析4. 语法分析树5. MySQL语法分析树生成过程6. 核心数据结构及其关系7. SQL解析的应用 2. 参考文档 0. 前言 你是否已经深入了解了MySQL中 SQL解析过程&#xff0c;以及解析过程中每个环节扮演的具体角色&#xff1f;你是否…

Windows SQLYog连接不上VMbox Ubuntu2204 的Mysql解决方法

Windows SQLYog连接不上VMbox Ubuntu2204 的Mysql解决方法 解决方法&#xff1a; 1、先检查以下mysql的端口状态 netstat -anp|grep mysql如果显示127.0.0.1:3306 则说明需要修改&#xff0c;若为: : :3306&#xff0c;则不用。 在**/etc/mysql/mysql.conf.d/mysqld.cnf**&am…

软件测试之黑盒测试、白盒测试分别是什么?有什么区别?

软件开发过程中&#xff0c;为了保证软件质量和稳定性&#xff0c;必须进行全面而细致的测试工作&#xff0c;而黑盒测试和白盒测试正是两种常用的测试方法。 一、黑盒测试 黑盒测试是一种基于软件外部功能的测试方法。测试人员对待测试的软件系统&#xff0c;就像一个黑匣子…

uniapp-秋云图表 ucharts echarts 对比与关系

科普&#xff1a; 秋云图表库&#xff0c;包含二种配置属性对应二种js配置文件。 一种是 &#xff1a;echarts.js,一种是 &#xff1a; ucharts。 二者的配置属性不一样&#xff01; ucharts和echarts对比 ucharts和echarts都是用于数据可视化的开源JavaScript库&#xff0c;它…

【高危】Apache Airflow Spark Provider 反序列化漏洞 (CVE-2023-40195)

zhi.oscs1024.com​​​​​ 漏洞类型反序列化发现时间2023-08-29漏洞等级高危MPS编号MPS-qkdx-17bcCVE编号CVE-2023-40195漏洞影响广度广 漏洞危害 OSCS 描述Apache Airflow Spark Provider是Apache Airflow项目的一个插件&#xff0c;用于在Airflow中管理和调度Apache Spar…

自动化机器学习Auto-Sklearn安装和使用教程

安装和使用 Auto-Sklearn Auto-sklearn 提供了开箱即用的监督型自动机器学习。从名字可以看出,auto-sklearn 是基于机器学习库 scikit-learn 构建的,可为新的数据集自动搜索学习算法,并优化其超参数。因此,它将机器学习使用者从繁琐的任务中解放出来,使其有更多时间专注于…

django/CVE-2017-12794XSS漏洞复现

docker搭建漏洞复现环境 漏洞原理看帮助文档 # Django debug page XSS漏洞&#xff08;CVE-2017-12794&#xff09;分析Django发布了新版本1.11.5&#xff0c;修复了500页面中可能存在的一个XSS漏洞&#xff0c;这篇文章说明一下该漏洞的原理和复现&#xff0c;和我的一点点评…

一种借助MYSQL递归CTE生成所有组合情况的实现方法

需求说明 有如下表和数据&#xff1a; Nname1户口2查询机构数过多3危险驾驶4多头用信 需要输出name里的所有组合情况&#xff0c;即单个值&#xff0c;两两组合&#xff0c;三个组合、四个组合。结果为2的n次方-1中情况&#xff0c;这里是15。 预期结果为&#xff1a; Com…

【OpenCV入门】第四部分——阈值

文章结构 阈值概述阈值处理函数二值化阈值处理二值化阈值处理反二值化处理 零处理低于阈值零处理超出阈值零处理 截断处理自适应处理Otsu方法 阈值概述 在PhotoShop里头&#xff0c;有一个工具可以快速抠出一幅图像中的轮廓&#xff0c;这个工具就是阈值。OpenCV也提供了阈值&…

springboot实战(二)之将项目上传至远程仓库

目录 环境&#xff1a; 背景&#xff1a; 操作&#xff1a; 1.注册码云账号 2.创建仓库 步骤&#xff1a; 1.注册完码云账号后&#xff0c;点击加号&#xff0c;新建仓库 2.输入项目名称和介绍&#xff0c;点击创建 3.复制仓库地址&#xff0c;你可以选择https协议或者…