结合项目——设计模式实现状态扭转

简述:

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

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的异味。

 

posted @ 2021-01-25 18:49  pmingup9012  阅读(394)  评论(0编辑  收藏  举报