背景:最近新接触一个web项目,要求新增加一个审批流程。简化抽象模型,订单分为三种A\B\C。其中AB的审批流程完全一致,简化为审批1->审批2->审批后动作。C的审批流程略有不同,简化为审批0->审批1->审批2->审批后动作。现在要做的一个功能是要在订单C上增加一个审批流程变为:审批0->审批3->审批1->审批2->审批后动作。
很简单的一个功能,却花费了我近两天的时间。时间主要花费在了读代码和重构代码上,下面说一下我的重构过程。
以前的代码就是一个大逻辑,先是if判断订单类型,再if判断订单状态,再执行对应审批流程。
if(订单类型==A){
if(!passed){
doUnPassed();
}
if(订单状态==审批1){
do审批1();
}else if(订单状态==审批2){
do审批2();
do审批完成();
}else{
throw Exception();
}
}else if ()......
很大的一个判断逻辑,可以想象到,最开始可能就是一个订单,一种审批流程,所以简单一个处理方法就满足要求。后来加了一个审批流程,就增加了一个else逻辑。再增加一种订单类型,在后面增加一个if else逻辑。久而久之,就变成了一个超级庞大的逻辑。也没人再敢改,牵一发动全身,并且这代码经过若干人手流转,也很难有人可以全面了解整体的流程了。
这次让我加一个审批逻辑,我就已经感觉到很困难了,所以我决定彻底重构。
首先理清楚整个代码逻辑,就是我前面所说的背景。
然后开始进行重构,可以看到三种订单类型,审批流程基本一致。很容易想到,第一步先把公共的流程剥离出来,由父类实现,个性化的流程由子类重写。这里就是模板模式,基本的类图如下:
第一步重构完成,类的结构变得比较清晰,流程也比较明确了,每种订单的个性化需求也满足了。调用起来却不太方便,还需要根据订单类型创建不同对象,很容易想到用工厂模式。三类订单审批类实现同一个接口,由工厂创建对应实例,再一次重构后类图:
简单的重构就算完成了,整个代码清爽了很多。但是作为追求完美的我不能这么满足。我们可以看到,如果增加一个审批流程依旧很麻烦,比如订单A需要增加一个审批流程4,那么需要修改抽象父类的审批流程逻辑、增加一个抽象方法审批4,每个子类都要实现这个抽象方法。
怎么处理,可以更方便的增加审批流程,每种订单之间不会相互影响。我们可以把审批流程作为一个责任链,订单的每种状态对应一个负责处理的单元。所有订单对应一个完整的责任链,再配合上状态模式,由订单当前的状态决定责任链中哪个单元来处理。这样某种订单增加一个审批流程,只需要给他增加一个状态,再在责任链中增加一个处理单元即可。如下简化后的代码
public class Service1 extends BaseApprovalService implements IService{
private Operator op;
private String state;
@Override
public void approval() {
super.doApproval(state);
}
@Override
protected void after() throws ServiceException{
//订单审批结束后执行
}
@Override
protected void before() throws ServiceException {
//订单审批前执行
}
}
抽象父类也变的简单明了:
public abstract class BaseApprovalService {
public void approvalOrder(String state) throws ServiceException {
before();
if(!passed){
//不通过处理
}else {
getOp().next(state).handler();
}
after();
}
protected abstract void after() throws ServiceException;
protected abstract void before() throws ServiceException;
}
至此,算是重构完成。增加新的订单流程变得简单方便,增加新的订单种类也很方便。而且留了个性化的接口before和after处理。
个人水平有限,只能重构到这样子了,看着新的代码还是感觉很开心。上学时候老师总说见字如见人,写的字能代表一个人。
现在作为程序猿,见代码如见人。