应用状态模式做项目审批流程

最近学完《Head first to Design Pattern》这本书,正好打算利用业余时间重写公司的一个项目,就想拿着重建项目的机会练下手,此文用来记录下遇到的坑和解决方案。

首先介绍下项目背景,一个很简单的流程,对象是Project,有一个标记审批状态的字段为status,根据业务流程,转到不同的阶段,每个阶段对应一个值,很适合用状态模式来实现。

贴一下设计模式书中的图:里面的术语下文要用到。

首先先定义接口,并写好实现类,然后把Context也写好,基本上都是照抄书上代码,毋庸多言。我唯一做了改动的是把State接口改成了普通类,底下的ConcreateState用的覆盖的方法。默认父类方法里都是抛出UnsupportedOperationException。这样如果调用子类没有实现的方法时,会自动抛出不支持的操作。

贴一下我的类图:

 那么这个状态机是如何跟project的状态挂接起来的呢,书中的糖果机只一个,而project有很多个,怎么让每个project都能用上状态模式呢?我绕了很多弯路,才发现之前的误区,project就是糖果,status就是count,状态机不需要有很多个,只要能处理这个状态就行了。

那么context的构造器方法就可以改造成传入project对象的构造器

    public StateContext(Project project) {
        initialState = new InitialState(this);
        approvalPendingState = new ApprovalPendingState(this);
        approvalFailState = new ApprovalFailState(this);
        biddingState = new BiddingState(this);
        bidFailState = new BidFailState(this);
        implementState = new ImplementState(this);
        finishState = new FinishState(this);
        closedState = new ClosedState(this);
        switch (project.getStatus()) {
            case Project.INITIAL:
                state = initialState;
                break;
            case Project.APPROVAL_PENDING:
                state = approvalPendingState;
                break;
            case Project.APPROVAL_FAIL:
                state = approvalFailState;
                break;
            case Project.BIDDING:
                state = biddingState;
                break;
            case Project.BID_FAIL:
                state = bidFailState;
                break;
            case Project.IMPLEMENT:
                state = implementState;
                break;
            case Project.FINISH:
                state = finishState;
                break;
            case Project.CLOSED:
                state = closedState;
                break;
        }
    }

然后把Project传入每个状态类,

    public Project apply(Project project){
       return  state.apply(project);
    }

在状态类内改变后再返回。 

    @Override
    public Project apply(Project project) {
        project.setStatus(Project.APPROVAL_PENDING);
        stateContext.setState(stateContext.getApprovalPendingState());
        return project;
    }

在Controller里如下图调用

		try {
			StateContext stateContext = new StateContext(project);
			projectService.save(stateContext.apply(project));
			addMessage(redirectAttributes, "保存项目成功");
		} catch (UnsupportedOperationException e) {
			addMessage(redirectAttributes, "当前阶段不支持此操作");
		}

之前我曾经想过把Service直接放到每个Concrete动作类里去,但是存在的问题是没法注入Service,因为都是构造方法构造出来的,然后我又改造构造方法,全部换成注入的,但是在注入Context里保存当前状态的state对象时遇到问题了,state是可变的,没法确定应该注入哪一个。后来全又改回书上的写法了。

另外状态模式也有框架,是在sprngboot下的,以后有机会可以尝试一下。最后写流程还是提倡使用工作流,使用状态模式的问题是类爆炸太多,写起来也不方便。

猜你喜欢

转载自my.oschina.net/u/2351812/blog/1649489