重学 Java 设计模式:实战状态模式「模拟系统营销活动,状态流程审核发布上线场景」


作者:小傅哥
博客:https://bugstack.cn - 原创系列专题文章

沉淀、分享、成长,让自己和他人都能有所收获!😄

一、前言

写好代码三个关键点

如果把写代码想象成家里的软装,你肯定会想到家里需要有一个非常不错格局最好是南北通透的,买回来的家具最好是品牌保证质量的,之后呢是大小合适,不能摆放完了看着别扭。那么把这一过程抽象成写代码就是需要三个核心的关键点;架构(房间的格局)、命名(品牌和质量)、注释(尺寸大小说明书),只有这三个点都做好才能完成出一套赏心悦目的

平原走码🐎易放难收

上学期间你写了多少代码?上班一年你能写多少代码?回家自己学习写了多少代码?个人素养的技术栈地基都是一块一块砖码出来的,写的越广越深,根基就越牢固。当根基牢固了以后在再上层建设就变得迎刃而解了,也更容易建设了。往往最难的就是一层一层阶段的突破,突破就像破壳一样,也像夯实地基,短时间看不到成绩,也看不出高度。但以后谁能走的稳,就靠着默默的沉淀。

技术传承的重要性

可能是现在时间节奏太快,一个需求下来恨不得当天就上线(这个需求很简单,怎么实现我不管,明天上线!),导致团队的人都很慌很急很累很崩溃,最终反反复复的人员更替,项目在这个过程中也交接了N次,文档不全、代码混乱、错综复杂,谁在后面接手也都只能修修补补,就像烂尾楼。这个没有传承、没有沉淀的项目,很难跟随业务的发展。最终!根基不牢,一地鸡毛。

二、开发环境

  1. JDK 1.8
  2. Idea + Maven
  3. 涉及工程三个,可以通过关注公众号bugstack虫洞栈,回复源码下载获取(打开获取的链接,找到序号18)
工程 描述
itstack-demo-design-19-00 场景模拟工程;模拟营销活动操作服务(查询、审核)
itstack-demo-design-19-01 使用一坨代码实现业务需求
itstack-demo-design-19-02 通过设计模式优化改造代码,产生对比性从而学习

三、状态模式介绍

状态模式,图片来自 refactoringguru.cn

状态模式描述的是一个行为下的多种状态变更,比如我们最常见的一个网站的页面,在你登录与不登录下展示的内容是略有差异的(不登录不能展示个人信息),而这种登录不登录就是我们通过改变状态,而让整个行为发生了变化。

收音机&放音机&磁带机

至少80后、90后的小伙伴基本都用过这种磁带放音机(可能没有这个好看),它的上面是一排按钮,当放入磁带后,通过上面的按钮就可以让放音机播放磁带上的内容(listen to 英语听力考试),而且有些按钮是互斥的,当在某个状态下才可以按另外的按钮(这在设计模式里也是一个关键的点)。

四、案例场景模拟

场景模拟;营销活动审核状态流转

在本案例中我们模拟营销活动审核状态流转场景(一个活动的上线是多个层级审核上线的)

在上图中也可以看到我们的流程节点中包括了各个状态到下一个状态扭转的关联条件,比如;审核通过才能到活动中,而不能从编辑中直接到活动中,而这些状态的转变就是我们要完成的场景处理。

大部分程序员基本都开发过类似的业务场景,需要对活动或者一些配置需要审核后才能对外发布,而这个审核的过程往往会随着系统的重要程度而设立多级控制,来保证一个活动可以安全上线,避免造成资损。

当然有时候会用到一些审批流的过程配置,也是非常方便开发类似的流程的,也可以在配置中设定某个节点的审批人员。但这不是我们主要体现的点,在本案例中我们主要是模拟学习对一个活动的多个状态节点的审核控制。

1. 场景模拟工程

itstack-demo-design-19-00
└── src
    └── main
        └── java
            └── org.itstack.demo.design
                ├── ActivityInfo.java
                ├── Status.java
                └── ActivityService.java
  • 在这个模拟工程里我们提供了三个类,包括;状态枚举(Status)、活动对象(ActivityInfo)、活动服务(ActivityService),三个服务类。
  • 接下来我们就分别介绍三个类包括的内容。

2. 代码实现

2.1 基本活动信息

public class ActivityInfo {

    private String activityId;    // 活动ID
    private String activityName;  // 活动名称
    private Enum<Status> status;  // 活动状态
    private Date beginTime;       // 开始时间
    private Date endTime;         // 结束时间
   
    // ...get/set
}  
  • 一些基本的活动信息;活动ID、活动名称、活动状态、开始时间、结束时间。

2.2 活动枚举状态

public enum Status {

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

}
  • 活动的枚举;1创建编辑、2待审核、3审核通过(任务扫描成活动中)、4审核拒绝(可以撤审到编辑状态)、5活动中、6活动关闭、7活动开启(任务扫描成活动中)

2.3 活动服务接口

public class ActivityService {

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

    public static void init(String activityId, Enum<Status> status) {
        // 模拟查询活动信息
        ActivityInfo activityInfo = new ActivityInfo();
        activityInfo.setActivityId(activityId);
        activityInfo.setActivityName("早起学习打卡领奖活动");
        activityInfo.setStatus(status);
        activityInfo.setBeginTime(new Date());
        activityInfo.setEndTime(new Date());
        statusMap.put(activityId, status);
    }

    /**
     * 查询活动信息
     *
     * @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);
    }

    /**
     * 执行状态变更
     *
     * @param activityId   活动ID
     * @param beforeStatus 变更前状态
     * @param afterStatus  变更后状态 b
     */
    public static synchronized void execStatus(String activityId, Enum<Status> beforeStatus, Enum<Status> afterStatus) {
        if (!beforeStatus.equals(statusMap.get(activityId))) return;
        statusMap.put(activityId, afterStatus);
    }

}
  • 在这个静态类中提供了活动的查询和状态变更接口;queryActivityInfoqueryActivityStatusexecStatus
  • 同时使用Map的结构来记录活动ID和状态变化信息,另外还有init方法来初始化活动数据。实际的开发中这类信息基本都是从数据库或者Redis中获取。

五、用一坨坨代码实现

这里我们先使用最粗暴的方式来实现功能

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

1. 工程结构

itstack-demo-design-19-01
└── src
    └── main
        └── java
            └── org.itstack.demo.design
                ├── ActivityExecStatusController.java
                └── Result.java
  • 整个实现的工程结构比较简单,只包括了两个类;ActivityExecStatusControllerResult,一个是处理流程状态,另外一个是返回的对象。

2. 代码实现

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", "非可处理的活动状态变更");

    }

}
  • 这里我们只需要看一下代码实现的结构即可。从上到下是一整篇的ifelse,基本这也是大部分初级程序员的开发方式。
  • 这样的面向过程式开发方式,对于不需要改动代码,也不需要二次迭代的,还是可以使用的(但基本不可能不迭代)。而且随着状态和需求变化,会越来越难以维护,后面的人也不好看懂并且很容易填充其他的流程进去。越来越乱就是从点滴开始的

3. 测试验证

3.1 编写测试类

@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));
}
  • 我们的测试代码包括了两个功能的验证,一个是从编辑中审核拒绝,另外一个是从编辑中到提交审核
  • 因为从我们的场景流程中可以看到,编辑中的活动是不能直接到审核拒绝的,这中间还需要提审

3.2 测试结果

23:24:30.774 [main] INFO  org.itstack.demo.design.test.ApiTest - 测试结果(编辑中To审核拒绝){"code":"0001","info":"变更状态拒绝"}
23:24:30.778 [main] INFO  org.itstack.demo.design.test.ApiTest - 测试结果(编辑中To提交审核){"code":"0000","info":"变更状态成功"}

Process finished with exit code 0
  • 从测试结果和我们的状态流程的流转中可以看到,是符合测试结果预期的。除了不好维护外,这样的开发过程还是蛮快的,但不建议这么搞!

六、状态模式重构代码

接下来使用状态模式来进行代码优化,也算是一次很小的重构。

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

1. 工程结构

itstack-demo-design-19-02
└── src
    └── main
        └── java
            └── org.itstack.demo.design
                ├── event
                │    ├── CheckState.java
                │    └── CloseState.java
                │    └── DoingState.java
                │    └── EditingState.java
                │    └── OpenState.java
                │    └── PassState.java
                │    └── RefuseState.java
                ├── Result.java
                ├── State.java
                └── StateHandler.java

状态模式模型结构

状态模式模型结构

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

2. 代码实现

2.1 定义状态抽象类

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);

}
  • 在整个接口中提供了各项状态流转服务的接口,例如;活动提审、审核通过、审核拒绝、撤审撤销等7个方法。
  • 在这些方法中所有的入参都是一样的,activityId(活动ID)、currentStatus(当前状态),只有他们的具体实现是不同的。

2.2 部分状态流转实现

编辑

public class EditingState extends State {

    public Result arraignment(String activityId, Enum<Status> currentStatus) {
        ActivityService.execStatus(activityId, currentStatus, Status.Check);
        return new Result("0000", "活动提审成功");
    }

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

    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", "编辑中不可撤销审核");
    }

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

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

    public Result doing(String activityId, Enum<Status> currentStatus) {
        return new Result("0001", "编辑中活动不可执行活动中变更");
    }

}

提审

public class CheckState extends State {

    public Result arraignment(String activityId, Enum<Status> currentStatus) {
        return new Result("0001", "待审核状态不可重复提审");
    }

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

    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", "活动审核撤销回到编辑中");
    }

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

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

    public Result doing(String activityId, Enum<Status> currentStatus) {
        return new Result("0001", "待审核活动不可执行活动中变更");
    }

}
  • 这里提供了两个具体实现类的内容,编辑状态和提审状态。
  • 例如在这两个实现类中,checkRefuse这个方法对于不同的类中有不同的实现,也就是不同状态下能做的下一步流转操作已经可以在每一个方法中具体控制了。
  • 其他5个类的操作是类似的具体就不在这里演示了,大部分都是重复代码。可以通过源码进行学习理解。

2.3 状态处理服务

public class StateHandler {

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

    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());   // 审核拒绝
    }

    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);
    }
    
}
  • 这是对状态服务的统一控制中心,可以看到在构造函数中提供了所有状态和实现的具体关联,放到Map数据结构中。
  • 同时提供了不同名称的接口操作类,让外部调用方可以更加容易的使用此项功能接口,而不需要像在itstack-demo-design-19-01例子中还得传两个状态来判断。

3. 测试验证

3.1 编写测试类(Editing2Arraignment)

@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.queryActivityInfo(activityId).getStatus()));
}

测试结果

23:59:20.883 [main] INFO  org.itstack.demo.design.test.ApiTest - 测试结果(编辑中To提审活动){"code":"0000","info":"活动提审成功"}
23:59:20.907 [main] INFO  org.itstack.demo.design.test.ApiTest - 活动信息:{"activityId":"100001","activityName":"早起学习打卡领奖活动","beginTime":1593694760892,"endTime":1593694760892,"status":"Check"} 状态:"Check"

Process finished with exit code 0
  • 测试编辑中To提审活动,的状态流转。

3.2 编写测试类(Editing2Open)

@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.queryActivityInfo(activityId).getStatus()));
}

测试结果

23:59:36.904 [main] INFO  org.itstack.demo.design.test.ApiTest - 测试结果(编辑中To开启活动){"code":"0001","info":"非关闭活动不可开启"}
23:59:36.914 [main] INFO  org.itstack.demo.design.test.ApiTest - 活动信息:{"activityId":"100001","activityName":"早起学习打卡领奖活动","beginTime":1593694776907,"endTime":1593694776907,"status":"Editing"} 状态:"Editing"

Process finished with exit code 0
  • 测试编辑中To开启活动,的状态流转。

3.3 编写测试类(Refuse2Doing)

@Test
public void test_Refuse2Doing() {
    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.queryActivityInfo(activityId).getStatus()));
}

测试结果

23:59:46.339 [main] INFO  org.itstack.demo.design.test.ApiTest - 测试结果(拒绝To活动中){"code":"0001","info":"审核拒绝不可执行活动为进行中"}
23:59:46.352 [main] INFO  org.itstack.demo.design.test.ApiTest - 活动信息:{"activityId":"100001","activityName":"早起学习打卡领奖活动","beginTime":1593694786342,"endTime":1593694786342,"status":"Refuse"} 状态:"Refuse"

Process finished with exit code 0
  • 测试拒绝To活动中,的状态流转。

3.4 编写测试类(Refuse2Revoke)

@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()));
}

测试结果

23:59:50.197 [main] INFO  org.itstack.demo.design.test.ApiTest - 测试结果(拒绝To撤审){"code":"0000","info":"撤销审核完成"}
23:59:50.208 [main] INFO  org.itstack.demo.design.test.ApiTest - 活动信息:{"activityId":"100001","activityName":"早起学习打卡领奖活动","beginTime":1593694810201,"endTime":1593694810201,"status":"Editing"} 状态:"Editing"

Process finished with exit code 0
  • 测试测试结果(拒绝To撤审),的状态流转。

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

七、总结

  • 从以上的两种方式对一个需求的实现中可以看到,在第二种使用设计模式处理后已经没有了ifelse,代码的结构也更加清晰易于扩展。这就是设计模式的好处,可以非常强大的改变原有代码的结构,让以后的扩展和维护都变得容易些。
  • 在实现结构的编码方式上可以看到这不再是面向过程的编程,而是面向对象的结构。并且这样的设计模式满足了单一职责开闭原则,当你只有满足这样的结构下才会发现代码的扩展是容易的,也就是增加和修改功能不会影响整体的变化。
  • 但如果状态和各项流转较多像本文的案例中,就会产生较多的实现类。因此可能也会让代码的实现上带来了时间成本,因为如果遇到这样的场景可以按需评估投入回报率。主要点在于看是否经常修改、是否可以做成组件化、抽离业务与非业务功能。

八、推荐阅读

小傅哥 CSDN认证博客专家 ASM 设计模式 bugstack
小傅哥多年从事一线互联网Java开发,旨在为大家提供一个清晰详细的学习教程,侧重点更倾向编写Java核心内容。如果能为您提供帮助,请给予支持(关注、点赞、分享)!在过去码文的一年里付出了所有的深夜和假期进行码文,创建了9个较大专题类文章;《用Java实现JVM》、《Netty4.x专题》、《中间件开发》、《领域驱动设计》、《全链路监控》等和近150篇原创,也整理了第一本关于字节码编程的PDF书籍,共计11万余字。同时建设了第一个关于:CodeGuide|程序员编码指南,的Git仓库,涵盖了;书籍、源码、案例、文章等各项学习拓展技能的资料。

全面解剖RocketMQ和项目实战

09-05
RocketMQ 是阿里巴巴在2012年开源的分布式消息中间件,目前已经捐赠给 Apache 软件基金会,并于2017年9月25日成为Apache 的顶级项目。作为经历过多次阿里巴巴双十一这种“超级工程”的洗礼并有稳定出色表现的国产中间件,以其高性能、低延时和高可靠等特性近年来已经也被越来越多的国内企业使用。其主要功能有1.灵活可扩展性、2.海量消息堆积能力、3.支持顺序消息、4.多种消息过滤方式、5.支持事务消息、6.回溯消费等常用功能。 RocketMQ 核心的四大组件:Name Server、Broker、Producer、Consumer ,每个组件都可以部署成集群进行水平扩展。 2、适应人群 有一定的Java基础,并且有分布式项目开发经验。 3、课程价值 可以让初学者对分布式系统解耦有一定认识,并且能够通过快速使用RocketMQ实现分布式服务的异步通信,同时本课程还会通过项目案例实战让学员对RocketMQ的应用场景有所体会,最后再通过源码角度让学员对RocketMQ的原理有所理解,不仅做到“知其然”,亦“知其所以然”。 4、课程收获 1. 理解消息中间件MQ的优势和应用场景 2. 掌握RocketMQ的核心功能,以及各种消息发送案例 3. 通过电商项目深刻理解RocketMQ在使用项目中的落地应用 4. 通过RocketMQ高级功能和源码学习,对RocketMQ的技术细节和原理有更加透彻的理解 5、课程亮点 l  核心功能 n  MQ介绍 n  环境准备 n  RocketMQ高可用集群搭建 n  各种消息发送样例 l  综合练习 n  项目背景介绍 n  功能分析 n  项目环境搭建 n  下单功能,保证各服务的数据一致性 n  确认订单功能,通过消息进行数据分发 n  整体联调 l  高级功能 n  消息的存储和发送 n  消息存储结构 n  刷盘机制 n  消息的同步复制和异步复制 n  负载均衡 l  源码分析 n  路由中心NameServer n  消息生产者Producer n  消息存储 n  消息消费Consumer 6、主讲内容 章节一:核心功能 1.     快速入门 a)     MQ介绍 b)     作用 c)      注意事项 d)     各MQ产品比较 2.     RocketMQ环境搭建 a)     环境准备 b)     安装RocketMQ c)      启动RocketMQ d)     测试RocketMQ e)     关闭RocketMQ 3.     RocketMQ高可用集群搭建 a)     集群各角色介绍 b)     集群搭建方式 c)      双主双从集群搭建 d)     集群监控平台 4.     各种消息发送样例 a)     同步消息 b)     异步消息 c)      单向消息 d)     顺序消息 e)     批量消息 f)      过滤消息 g)     事务消息 章节二:项目实战 1.    项目背景介绍 (1)    电商高可用MQ实战 2.    功能分析 (1)    下单功能 (2)    支付功能 3.    项目环境搭建 (1)    SpringBoot (2)    Dubbo (3)    Zookeeper (4)    RocketMQ (5)    Mysql 4.下单功能,保证各服务的数据一致性 5.确认订单功能,通过消息进行数据分发 章节三:高级功能 1. 消息的存储和发送 2. 消息存储结构 3. 刷盘机制 (1)    同步刷盘 (2)    异步刷盘 4. 消息的同步复制和异步复制 5. 负载均衡 (1)    Producer负载均衡 (2)    Consumer负载均衡 章节四:源码分析 1.     路由中心NameServer a)     NameServer架构设计 b)     NameServer启动流程 c)      NameServer路由注册和故障剔除 2.     消息生产者Producer a)     生产者启动流程 b)     生产者发送消息流程 c)      批量发送 3.     消息存储 a)     消息存储流程 b)     存储文件与内存映射 c)      存储文件 d)     实时更新消息消费队列和存储文件 e)     消息队列与索引文件恢复 f)      刷盘机制 4.     过期文件删除机制 a)     消息消费Consumer b)     消费者启动流程 c)      消息拉取 d)     消息队列负载均衡和重新分布机制 e)     消息消费过程 f)      定时消息机制 g)     顺序消息

Springboot+MyBatis企业级RESTful API开发项目实战/java视频教程

01-30
课程简介 这是一门使用Java语言,SpringBoot框架,从0开发一个RESTful API应用,接近企业级的项目(我的云音乐),课程包含了基础内容,高级内容,项目封装,项目重构等知识,99%代码为手写;因为这是项目课程;所以不会深入到源码讲解某个知识点,以及原理,但会粗略的讲解下基础原理;主要是讲解如何使用系统功能,流行的第三方框架,第三方服务,完成接近企业级项目,目的是让大家,学到真正的企业级项目开发技术。 适用人群 刚刚毕业的学生 想提高职场竞争力 想学从零开发SpringBoot项目 想提升SpringBoot项目开发技术 想学习SpringBoot项目架构技术 想学习企业级项目开发技术 就是想学习SpringBoot开发 能学到什么 从0开发一个类似企业级项目 学会能做出市面上90%通用API 快速增加1到2年实际开发经验 刚毕业学完后能找到满意的工作 已经工作学完后最高涨薪30% 课程信息 全课程目前是82章,155小时,每节视频都经过精心剪辑。 在线学习分辨率最高1080P 课程知识点 1~11章:学习方法,项目架构,编码规范,Postman使用方法,Git和Github版本控制 12~16章:搭建开发环境,快速入门SpringBoot框架 17~20章:快速入门MySQL数据库 21~30章:MyBatis,登录注册,找回密码,发送短信,发送邮件,企业级接口配置 31~41章:实现歌单,歌单标签,音乐,列表分页,视频,评论,好友功能 42~48章:阿里云OSS,话题,MyBatis-plus,应用监控 49~53章:Redis使用,集成Redis,SpringCache,HTTP缓存 54~58章:Elasticsearch使用,集成Elasticsearch,使用ES搜索 59~61章:商城,集成支付宝SDK,支付宝支付 62~64章:常用哈希和加密算法,接口加密和签名 65~67章:实时挤掉用户,企业级项目测试环境,企业级接口文档 68~69章:SpringBoot全站HTTPS,自签证书,申请免费证书 70~73章:云MySQL数据库,云Redis数据库使用,轻量级应用部署环境,域名解析 74~80章:Docker使用,生产级Kubernetes集群,域名解析,集群全站HTTPS 81~82章:增强和重构项目,课程总结,后续学习计划

图解Java数据结构和算法

06-21
1.算法是程序的灵魂,优秀的程序在对海量数据处理时,依然保持高速计算,就需要高效的数据结构和算法支撑。2.网上数据结构和算法的课程不少,但存在两个问题:1)授课方式单一,大多是照着代码念一遍,数据结构和算法本身就比较难理解,对基础好的学员来说,还好一点,对基础不好的学生来说,基本上就是听天书了2)说是讲数据结构和算法,但大多是挂羊头卖狗肉,算法讲的很少。 本课程针对上述问题,有针对性的进行了升级 3)授课方式采用图解+算法游戏的方式,让课程生动有趣好理解 4)系统全面的讲解了数据结构和算法, 除常用数据结构和算法外,还包括程序员常用10大算法:二分查找算法(非递归)、分治算法、动态规划算法、KMP算法、贪心算法、普里姆算法、克鲁斯卡尔算法、迪杰斯特拉算法、弗洛伊德算法、马踏棋盘算法。可以解决面试遇到的最短路径、最小生成树、最小连通图、动态规划等问题及衍生出的面试题,让你秒杀其他面试小伙伴3.如果你不想永远都是代码工人,就需要花时间来研究下数据结构和算法。教程内容:本教程是使用Java来讲解数据结构和算法,考虑到数据结构和算法较难,授课采用图解加算法游戏的方式。内容包括: 稀疏数组、单向队列、环形队列、单向链表、双向链表、环形链表、约瑟夫问题、栈、前缀、中缀、后缀表达式、中缀表达式转换为后缀表达式、递归与回溯、迷宫问题、八皇后问题、算法的时间复杂度、冒泡排序、选择排序、插入排序、快速排序、归并排序、希尔排序、基数排序(桶排序)、堆排序、排序速度分析、二分查找、插值查找、斐波那契查找、散列、哈希表、二叉树、二叉树与数组转换、二叉排序树(BST)、AVL树、线索二叉树、赫夫曼树、赫夫曼编码、多路查找树(B树B+树和B*树)、图、图的DFS算法和BFS、程序员常用10大算法、二分查找算法(非递归)、分治算法、动态规划算法、KMP算法、贪心算法、普里姆算法、克鲁斯卡尔算法、迪杰斯特拉算法、弗洛伊德算法马踏棋盘算法。学习目标:通过学习,学员能掌握主流数据结构和算法的实现机制,开阔编程思路,提高优化程序的能力。
©️2020 CSDN 皮肤主题: 数字20 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值