Skip to content

Latest commit

 

History

History
407 lines (329 loc) · 12.9 KB

19. 设计模式第十九节-状态模式.md

File metadata and controls

407 lines (329 loc) · 12.9 KB

设计模式第十九节

状态模式

引言

老话说的好:“写好代码需要三个关键。

如果我们把写代码当成是装修,那么我们需要一个格局不错并且南北通透,买回来的家具一定要有质量保证,与房屋的设计一致。那么代码里面就是需要保证:架构命名注释,这样才能写出一套赏心悦目的代码。

老话说的好:“代码不是艺术品、是手工活,得多写!

上学的时候你写了多少代码?上班一年你能写多少代码?回家自己学习你写了多少代码?个人素养的技术栈基地都是靠一行一行代码写出来的,写的越广越深,根基就能越加强壮。当根基强壮了,在进行上层建筑就变的简单起来。往往最难的就是一层一层阶段的突破,突破就像破壳一样,也像夯实地基,短时间看不到成绩,也看不出高度。但以后谁能走的稳,就靠着默默的沉淀。

老话说:“技术是需要传承的。

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

状态模式 介绍

wRhU8.png

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

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

场景模拟

介绍

wRwiU.png

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

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

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

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

场景模拟

代码目录

- state-mode-1
  - src.main.java.com.hcy
    - ActivityInfo.java
    - ActivityService.java
    - Status.java

代码

import java.util.Date;
/**
 * 基本活动信息
 * @author HCY
 * @since 2021/4/24 7:59 下午
*/
public class ActivityInfo {

    /**
     * 活动ID
     */
    private String activityId;
    /**
     * 活动名称
     */
    private String activityName;
    /**
     * 活动状态
     */
    private Enum<Status> status;
    /**
     * 开始时间
     */
    private Date beginTime;
    /**
     * 结束时间
     */
    private Date endTime;

    public String getActivityId() {
        return activityId;
    }

    public void setActivityId(String activityId) {
        this.activityId = activityId;
    }

    public String getActivityName() {
        return activityName;
    }

    public void setActivityName(String activityName) {
        this.activityName = activityName;
    }

    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;
    }
}
import java.util.Date;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 活动服务接口
 * @author HCY
 * @since 2021/4/24 8:00 下午
*/
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);
    }

}
/**
 * 活动枚举状态
 * @author HCY
 * @since 2021/4/24 8:00 下午
*/
public enum Status {

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

}

正常解决方案

代码目录

- state-mode-2
  - src.main.java.com.hcy
    - ActivityExecStatusController.java
    - Result.java

代码实现

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

    }

}

状态模式 重构代码

代码目录

- state-mode-3
  - src.main.java.com.hcy
    - impl
      - CheckState.java
      - CloseState.java
      - DoingState.java
      - EditingState.java
      - OpenState.java
      - PassState.java
      - RefuseState.java
    - Result.java
    - State.java
    - StateHandler.java

代码实现

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

}

总结

- 从以上的两种方式对一个需求的实现中可以看到,在第二种使用设计模式处理后已经没有了ifelse,代码的结构也更加清晰易于扩展。这就是设计模式的好处,可以非常强大的改变原有代码的结构,让以后的扩展和维护都变得容易些。

- 在实现结构的编码方式上可以看到这不再是面向过程的编程,而是面向对象的结构。并且这样的设计模式满足了单一职责和开闭原则,当你只有满足这样的结构下才会发现代码的扩展是容易的,也就是增加和修改功能不会影响整体的变化。