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