设计模式之状态模式


一、状态模式介绍

状态模式描述的是行为导致对象的状态变更。例如,在某个审核流程中,审核人员在待审核列表点击审核通过,那么相应审核项的状态就由待审核状态变成了审核通过状态,审核人员在待审核列表点击审核不通过,那么相应审核项的状态就由待审核状态变成了审核不通过状态

二、需求审批状态流转场景

本案例模拟一个普通的OA申请流程,假设一个需求从创建到完成需要经过多级审核。

1.审批流程

审批流程如下:
在这里插入图片描述

2.设计实现

状态枚举:

@Getter
public enum DemandState {
    // 状态枚举
    TOBE_CONFIRMED(10, "待确认"),
    TOBE_APPLIED(20, "待申请"),
    TOBE_AUDITED(30, "待审核"),
    AUDIT_PASS(40, "审核通过"),
    AUDIT_REFUSE(50, "审核驳回"),
    RET_BACK(60, "退回");

    private Integer state;
    private String desc;

    DemandState(Integer state, String desc) {
        this.state = state;
        this.desc = desc;
    }
}

事件枚举:

@Getter
public enum EventType {
    // 事件枚举
    CONFIRM(10, "确认需求"),
    COMMIT(20, "提交审核"),
    AUDIT_PASS(30, "审核通过"),
    AUDIT_REFUSE(40, "审核不通过"),
    REFUSED_THEN_UPDATE(50, "提交修改"),
    REFUSED_THEN_RET_BACK(60, "退回"),
    NOT_CONFIRM(70, "退回"),
    RET_BACK_THEN_UPDATE(80, "提交修改"),
    ;
    private Integer type;
    private String desc;

    EventType(Integer type, String desc) {
        this.type = type;
        this.desc = desc;
    }
}

对象实体:

@Data
public class Demand {
	
	/**
	 * 对象唯一标识
	 */
    private Long id;
    /**
     * 对象当前状态
     */
    private Integer state;
    /**
     * 其他的一些信息
     */
    private String otherInfos;
}

上下文:
上下文中包含需要进行状态流转的对象以及对应的事件。

@Getter
@Setter
public class Context {

    private Demand demand;

    private Integer eventType;
    
}

状态流转:

@Getter
@AllArgsConstructor
public abstract class Transition {
	
	/**
     * 当前状态
     */
    private final Integer currentState;
    /**
     * 下一个状态
     */
    private final Integer nextState;
	/**
     * 创建状态流转
     */
	public static Transition transition(DemandState currentState, DemandState nextState) {
        return new Transition(currentState.getState(), nextState.getState());
    }
}

事件:
事件在发生状态流转之前,可能会有一些前置和后置的操作,因此设计一个事件基类来提供这种扩展能力。

@Slf4j
@Getter
public abstract class Event {
    /**
     * 事件类型
     */
    private final Integer eventType;
    /**
     * 状态流转
     */
    private final Transition transition;

    protected Event(EventType eventType, Transition transition) {
        this.eventType = eventType.getType();
        this.transition = transition;
    }

    /**
     * 状态流转前操作
     */
    public void beforeTransit(Context ctx) {
        // 判断当前对象状态是否合法
        if (!transition.getCurrentState().equals(ctx.getDemand().getState())) {
            throw new IllegalArgumentException("需求当前状态[" + ctx.getDemand().getState() + "]不合法");
        }
    }

    /**
     * 状态流转
     */
    public void transit(Context ctx) {
        log.info("执行状态变更:{} -> {}", transition.getCurrentState(), transition.getNextState());
        ctx.getDemand().setState(transition.getNextState());
    }

    /**
     * 状态流转后操作
     */
    public void postTransit(Context ctx) {

    }
}

然后根据具体的时间定义出具体的事件类,这里给出几个:

/**
 * 确认需求事件
 */
public class ConfirmEvent extends Event {

    public ConfirmEvent() {
        super(EventType.CONFIRM, Transition.transition(DemandState.TOBE_CONFIRMED, DemandState.TOBE_APPLIED));
    }
}

/**
 * 提交审核事件
 */
public class CommitEvent extends Event {
    public CommitEvent() {
        super(EventType.COMMIT, Transition.transition(DemandState.TOBE_APPLIED, DemandState.TOBE_AUDITED));
    }
}

/**
 * 审核通过事件
 */
public class AuditPassEvent extends Event {

    public AuditPassEvent() {
        super(EventType.AUDIT_PASS, Transition.transition(DemandState.TOBE_AUDITED, DemandState.AUDIT_PASS));
    }
}

定义一个状态机,触发事件:

public class StateMachine {
    
    public static void trigger(Context ctx) {
        Event event = EventFactory.getEvent(ctx.getEventType());
        if (event != null) {
            event.beforeTransit(ctx);
            event.transit(ctx);
            event.postTransit(ctx);
        }
    }

    public static class EventFactory {
        private static final Map<Integer, Event> EVENT_MAP;

        static {
            EVENT_MAP = new HashMap<>();
            // 这里需要引入org.reflections:reflections:0.10.2
            Reflections reflections = new Reflections("com.example.study.dp.state.event");
            Set<Class<? extends Event>> subTypesOfEvent = reflections.getSubTypesOf(Event.class);
            subTypesOfEvent.forEach(subType->{
                try {
                    Event event = subType.newInstance();
                    EVENT_MAP.put(event.getEventType(), event);
                } catch (Exception e) {

                }
            });
        }

        public static Event getEvent(Integer eventType) {
            return EVENT_MAP.get(eventType);
        }

    }
}

3.测试

public class Main {

    public static Demand initDemand() {
        Demand demand = new Demand();
        demand.setId(1L);
        demand.setState(DemandState.TOBE_CONFIRMED.getState());
        return demand;
    }

    public static Context initContext(Integer eventType) {
        Context context = new Context();
        context.setDemand(initDemand());
        context.setEventType(eventType);
        return context;
    }

    public static void main(String[] args) {
        // 需求确认事件
        Context context = initContext(EventType.CONFIRM.getType());
        StateMachine.trigger(context);
        // 提交审核事件
        context.setEventType(EventType.COMMIT.getType());
        StateMachine.trigger(context);

        // 再次提交审核,这里会报错
        context.setEventType(EventType.COMMIT.getType());
        StateMachine.trigger(context);
    }
}

测试结果:
在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值