zoukankan      html  css  js  c++  java
  • 结合项目——设计模式实现状态扭转

    简述:

      在完成需求时需要完成一项状态扭转的功能,一个项目有多个阶段,如:项目开始创建 -> 设计完成 -> 申请实施 -> 完成施工,当从一个阶段跳转到另一个阶段时,会涉及到状态的改变,所以当需要状态改变时,需要完成下面几个步骤:

    1.首先应该校验要改变那条状态的数据。

    2.然后判断状态改变是否合规。

    3.接着改变数据库中那条数据的状态。

    4.最后插入操作日志。

    思考:

      由于功能点比较简单,可以直接进行if...else...判断做校验,然后更新数据,并插入日志。

      但随着项目迭代,在二期需求的时候,其中又加了几个状态,比如在开始创建和设计完成之间,需要加一个设计审批的状态,或者子任务状态,且每一个阶段都能进行项目驳回,那么如果按照第一期的设计,就只有在每个状态扭转的方法中添加if...else...选项,且因为子任务不在同一个类中,可能导致插入日志时的代码重复。

      怎么结合设计模式进行统一处理?

      实现:项目创建可能到下一阶段(项目设计)或者被驳回,项目设计可能到项目申请阶段或者被驳回,项目申请可能到施工阶段或者被驳回。

      1.项目创建 -> 项目设计/项目驳回

      2.项目设计 -> 项目申请/项目驳回

      3.项目申请 -> 项目施工/项目驳回

    实现:

    建立一个工厂,其中放入两个map,一个是存当前状态和下一个状态关系的map,一个是存操作动作的map(可以路由到对应实例),最后统一执行:

    @Slf4j
    @Component
    public class StatusChangeFactory {
    
        @Autowired
        private DemoCreateChangeStatusProcess demoCreateChangeStatusProcess;
    
        /**
         * 状态扭转关系map
         */
        private final Map<Integer, List<Integer>> statusChangeAllowMap = new HashMap<>(16);
        /**
         * 动作执行map
         */
        private final Map<DemoProjectActionEnum, ChangeStatusBaseProcess> processorMap = new HashMap<>(16);
    
        @PostConstruct
        public void init() {
            //将前后状态在map中建立关系,方便统一校验
            statusChangeAllowMap.put(
                    DemoProjectStatusEnum.CREATED.getStatus(),
                    Arrays.asList(
                            DemoProjectStatusEnum.EDIT_PROJECT_STATUS_TO_DESIGN.getStatus(),
                            DemoProjectStatusEnum.REJECTED.getStatus()
    
                    )
            );
    
            statusChangeAllowMap.put(
                    DemoProjectStatusEnum.EDIT_PROJECT_STATUS_TO_DESIGN.getStatus(),
                    Arrays.asList(
                            DemoProjectStatusEnum.EDIT_PROJECT_STATUS_TO_APPLICATION.getStatus(),
                            DemoProjectStatusEnum.REJECTED.getStatus()
                    )
            );
            //...
    
            //将processor与动作关联,通过动作执行不同的操作
            processorMap.put(DemoProjectActionEnum.CREATED, demoCreateChangeStatusProcess);
            //...
        }
    
        public void run(DemoBaseProjectDTO demoBaseProjectDTO) {
            //取出执行器去执行相关动作(简单工厂模式)
            ChangeStatusBaseProcess processor = processorMap.get(demoBaseProjectDTO.getKeyAction());
            //可以做一些公共参数统一插入,如下:
            demoBaseProjectDTO.setOpName("Mr A");
            demoBaseProjectDTO.setStatusChangeAllowMap(this.statusChangeAllowMap);
            demoBaseProjectDTO.setCurrentStatus(demoBaseProjectDTO.getStatus());
            demoBaseProjectDTO.setToStatus(demoBaseProjectDTO.getToStatus());
            //最后统一执行
            processor.handle(demoBaseProjectDTO);
        }
    }

    定义一个抽象类,做统一处理的模板:

    /** 对应1级类DTO **/
    public
    abstract class AbstractChangeStatusProcess<T extends ChangeStatusBaseDTO> { public void handle(T changeStatusBaseDTO) { before(changeStatusBaseDTO); doing(changeStatusBaseDTO); after(changeStatusBaseDTO); } /** * 前置事件 * @param changeStatusBaseDTO */ protected void before(T changeStatusBaseDTO) { } /** * 主逻辑 * @param changeStatusBaseDTO */ protected abstract void doing(T changeStatusBaseDTO); /** * 后置事件 * @param changeStatusBaseDTO */ protected abstract void after(T changeStatusBaseDTO); }

    因为会有多个不同的项目涉及到状态扭转,所以每个项目建立一个基础的process,继承抽象类:

    /** 对应2级类DTO **/
    public
    class ChangeStatusBaseProcess<T extends DemoBaseProjectDTO> extends AbstractChangeStatusProcess<T> { @Override protected void before(T changeStatusBaseDTO) { //如:校验数据,校验状态 } @Override protected void doing(T changeStatusBaseDTO) { //如:更新数据 } @Override protected void after(T changeStatusBaseDTO) { //如:更新日志 } }

    process的子类,用来实现每个动作,扭转对应状态:

    /** 对应3级类DTO **/
    @Component("DemoChangeStatusProcess") @Slf4j public class DemoCreateChangeStatusProcess extends ChangeStatusBaseProcess<DemoProjectDTO> { }

    最后附上对应的DTO和枚举:

    /**简介:所有项目的基类DTO (1级类DTO)**/
    @Data @NoArgsConstructor @AllArgsConstructor
    public class ChangeStatusBaseDTO implements Serializable { /** * 当前状态 */ private Integer currentStatus; /** * 下一个状态 */ private Integer toStatus; /** * 状态结构表 */ private Map<Integer, List<Integer>> statusChangeAllowMap; }
    /** 2级类DTO **/
    @Data @Accessors(chain
    = true)public class DemoBaseProjectDTO extends ChangeStatusBaseDTO implements Serializable { private Long id; private String opName; private String opId; private String keyAction; private String keyReason; private Integer status; }
    /** 3级类DTO **/
    @Data @Accessors(chain
    = true) public class DemoProjectDTO extends DemoBaseProjectDTO implements Serializable { }

    枚举:

    @Getter
    @AllArgsConstructor
    public enum DemoProjectActionEnum {
        CREATED(1, "创建"),
        UPDATED_DESIGN(2, "项目设计"),
        UPDATED_APPLICATION(3, "项目申请"),
        UPDATED_CONSTRUCTION(4, "项目施工"),
        REJECTED(10, "项目驳回"),
        ;
        private Integer code;
        private String action;
    }
    @Getter
    @AllArgsConstructor
    public enum DemoProjectStatusEnum {
        CREATED(10, "创建"),
        EDIT_PROJECT_STATUS_TO_DESIGN(20, "完成项目设计"),
        EDIT_PROJECT_STATUS_TO_APPLICATION(30, "完成项目申请"),
        EDIT_PROJECT_STATUS_TO_CONSTRUCTION(40, "完成项目施工"),
        REJECTED(100, "项目驳回"),
        ;
        private Integer status;
        private String message;
    }

    小结:

      改进后:

      1.是方便增删状态,实现开闭原则。

      2.是减少了重复代码.

      3.是去除了if...else的异味。

  • 相关阅读:
    linux C/C++编程之库-动态库,静态库创建及使用
    类linux 系统iptables 系统初始化配置
    OS error set
    OpenWrt修改
    OpenWrt backfire trunk源码下载及编译
    OpenWrt compiles
    OpenWrt 学习网址
    nginx编译配置
    cocos2d-x中的坐标系
    SGU 231 Prime Sum 求&lt;=n内有多少对素数(a,b)使得a+b也为素数 规律题
  • 原文地址:https://www.cnblogs.com/wencheng9012/p/14326837.html
Copyright © 2011-2022 走看看