最近学完《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下的,以后有机会可以尝试一下。最后写流程还是提倡使用工作流,使用状态模式的问题是类爆炸太多,写起来也不方便。