设计模式的状态模式,就是把状态独立成一个类,代替传统复杂的if-else。
网上还有非常多关于状态模式的介绍,不过很多都是点到即止,例子我理解的也不是很清楚。设计模式只是一种思路,掌握好这个思路就可以,实现有非常多种方法。
接下来我有项目经典问题-审批流程,来使用下状态模式。
基本的审批流程如下: 提交表单 -> 一级审批 -> 二级审批 -> 结束
那对应的我也设置了四个状态: 提交表单状态、一级审批状态、二级审批状态、结束状态。
状态定义了三个动作: 同意并进入下一环节(agreeToNext)、驳回上一环节(backToBefore)、结束(finish)
接下来我先定义状态的父类,这里我用到了抽象类,而非接口,因为我想状态有很多通用属性。很多网上例子会把这些属性放到Context上下文里面,通过改变状态去修改这些属性,我觉得都可以。
此父类声明了很多状态的属性,以及定义了三个动作,但是我把结束动作方法给实现了,因为我觉得接收动作代码都是相同的。
/**
* 提交状态0->一级审批1->二级审批2 ->审批完成3
* 状态
*/
public abstract class Status {
/**
* 同意并进入下一环节
* @param task 任务
* @param msg 意见
* @param nextUser 下一环节处理人
*/
public abstract boolean agreeToNext(ApproveTask task, String msg, String nextUser);
/**
* 打回上一环节
* @param task 任务
* @param msg 意见
* @return
*/
public abstract boolean backToBefore(ApproveTask task, String msg);
//属性
String statusName;//状态名
String user;//操作用户
String suggest;//意见信息
String request;//申请信息
boolean operationFlag;//是否进行了操作
int statusId = -1;
boolean agreeFlag;//是否同意
boolean isFinish;//是否终止状态
int nextStatusId = -1;//下一环节 状态id
/**
* 构造方法
*/
public Status(String user,String request) {
= user;
= request;
}
/**
* 结束
* @param task 任务
* @param msg 意见
*/
public boolean finish(ApproveTask task,String msg,boolean agreeFlag){
("环节结束 是否异常:"+agreeFlag);
().add(this);//放入审批链中
= msg;
= agreeFlag;
= 3;
= true;
//异常终止
FinishStatus finishStatus = new FinishStatus(,msg,agreeFlag);
(finishStatus);
().add(finishStatus);
return true;
}
@Override
public String toString() {
return "状态名:"+statusName +" 状态id:"+statusId+" 下一环节id:"+
nextStatusId+" 操作用户:"+user
+" 是否进行了操作:"+operationFlag
+" 是否同意:"+agreeFlag
+" 申请信息:"+request
+" 意见信息:"+ suggest;
}
}
接下来是具体的状态类:
提交状态类
/**
* 提交状态0->一级审批1->二级审批2 ->审批完成3
*
* 此类是提交状态
*
*/
public class SubmitStatus extends Status{
public SubmitStatus(String user,String request) {
super(user,request);
= "提交状态";
= 0;
}
@Override
public boolean agreeToNext(ApproveTask task,String msg,String nextUser){
("提交环节同意");
= true;
= true;//
= msg;
= statusId + 1;//下级状态
().add(this);//放入审批路径
(new FirstApproveStatus(nextUser,suggest));//设置下级审批,当前的意见成为下一审批人的申请信息
return true;
}
@Override
public boolean backToBefore(ApproveTask task,String msg){
("提交环节,不能打回");
return false;
}
}
一级审批状态类:
/**
* 提交状态0->一级审批1->二级审批2 ->审批完成3
* 一级审批
*/
public class FirstApproveStatus extends Status{
/**
* 构造方法
*
* @param user
*/
public FirstApproveStatus(String user,String request) {
super(user,request);
= "一级审批";
= 1;
}
@Override
public boolean agreeToNext(ApproveTask task, String msg, String nextUser) {
("一级审批同意");
= true;//
= true;
= msg;
= statusId + 1;//下级状态
().add(this);//放入审批路径
(new SecondApproveStatus(nextUser,));//设置下级审批
return true;
}
@Override
public boolean backToBefore(ApproveTask task, String msg) {
("一级审批回退");
= false;
= true;
= msg;
= statusId -1;
String beforeUserId = ().get(().size() - 1).user;
().add(this);
(new SubmitStatus(beforeUserId,));
return true;
}
}
二级审批状态类:
public class SecondApproveStatus extends Status {
/**
* 构造方法
*
* @param user
*/
public SecondApproveStatus(String user,String request) {
super(user,request);
= "二级审批";
= 2;
}
@Override
public boolean agreeToNext(ApproveTask task, String msg, String nextUser) {
("二级审批同意");
/* = true;//
= msg;
= statusId + 1;//下级状态
().add(this);//放入审批路径
Status finishStatus = new FinishStatus(,,true);
(finishStatus);//设置下级审批
().add(finishStatus);//放入审批路径
*/
(task,msg,true);
return true;
}
@Override
public boolean backToBefore(ApproveTask task, String msg) {
("二级审批回退");
= false;
= msg;
= statusId -1;
= true;
String beforeUserId = ().get(().size() - 1).user;
().add(this);
(new FirstApproveStatus(beforeUserId,));
return true;
}
}
结束状态类:
/**
* 提交状态0->一级审批1->二级审批2 ->审批完成3
* 一级审批
*/
public class FirstApproveStatus extends Status{
/**
* 构造方法
*
* @param user
*/
public FirstApproveStatus(String user,String request) {
super(user,request);
= "一级审批";
= 1;
}
@Override
public boolean agreeToNext(ApproveTask task, String msg, String nextUser) {
("一级审批同意");
= true;//
= true;
= msg;
= statusId + 1;//下级状态
().add(this);//放入审批路径
(new SecondApproveStatus(nextUser,));//设置下级审批
return true;
}
@Override
public boolean backToBefore(ApproveTask task, String msg) {
("一级审批回退");
= false;
= true;
= msg;
= statusId -1;
String beforeUserId = ().get(().size() - 1).user;
().add(this);
(new SubmitStatus(beforeUserId,));
return true;
}
}
接下来是任务类,也就是很多网络例子的Context上下文 或环境。
这个类有当前的状态、还有整个审批状态流的属性,还有提供对外的 同意、驳回、拒绝等操作,外部只需要对此类进行操作即可。
import ;
import ;
/**
* 审批任务 相当于 状态模式的Context 环境
*
*/
public class ApproveTask {
private Status status;//当前的状态
private List<Status> approveStatusList = new ArrayList<>();//审批操作人 此处可以扩展
/**
* 任务 构造函数
*/
public ApproveTask(String user){
= new SubmitStatus(user,null);
}
/**
* 同意 要写同意意见
* @param msg 同意意见
*/
public void agree(String msg,String nextUser){
(this,msg,nextUser);
}
/**
* 驳回
* @param msg
*/
public void back(String msg){
(this,msg);
}
public void refuse(String msg){
(this,msg,false);
}
/**
* 打印审批路径
*/
public void showApprovePath(){
().forEach(::println);
}
//getter and setter
public Status getStatus() {
return status;
}
public void setStatus(Status status) {
= status;
}
public List<Status> getApproveStatusList() {
return approveStatusList;
}
public void setApproveStatusList(List<Status> approveStatusList) {
= approveStatusList;
}
}
测试方法1(正常二级审批流程):
public static void normal(){
("---------正常流程----------");
ApproveTask approveTask = new ApproveTask("小红");//小红新建个审批任务
("由于工作需要,申请对工程机进行使用 3天","小明");//小红写申请书,提交给 小明
("小红需要对工程师进行使用,初步决定可以","小亮");//小明一级审批同意,并发送给小亮
("情况属实,可以批准",null);//小亮审批同意
();
("----整体审批路径如下----");
();
}
运行结果:
---------正常流程----------
提交环节同意
一级审批同意
二级审批同意
环节结束 是否异常:true
----整体审批路径如下----
状态名:提交状态 状态id:0 下一环节id:1 操作用户:小红 是否进行了操作:true 是否同意:true 申请信息:null 意见信息:由于工作需要,申请对工程机进行使用 3天
状态名:一级审批 状态id:1 下一环节id:2 操作用户:小明 是否进行了操作:true 是否同意:true 申请信息:由于工作需要,申请对工程机进行使用 3天 意见信息:小红需要对工程师进行使用,初步决定可以
状态名:二级审批 状态id:2 下一环节id:3 操作用户:小亮 是否进行了操作:true 是否同意:true 申请信息:小红需要对工程师进行使用,初步决定可以 意见信息:情况属实,可以批准
状态名:结束环境 状态id:-1 下一环节id:-1 操作用户:小亮 是否进行了操作:true 是否同意:true 申请信息:情况属实,可以批准 意见信息:null
测试方法2(驳回例子):
public static void back(){
("---------驳回流程--------");
("新建任务");
ApproveTask approveTask = new ApproveTask("小红");//小红新建个审批任务
("现在的任务状态:"+());
("由于工作需要,申请对工程机进行使用 3天","小明");//小红写申请书,提交给 小明
("现在的任务状态:"+());
("小红需要对工程师进行使用,初步决定可以","小亮");//小明一级审批同意,并发送给小亮
("现在的任务状态:"+());
("不行呀,请再检查检查");//小亮 二级审批不同意
("现在的任务状态:"+());
("已经检查过库存了,确定可以","小亮");//小明一级审批再次同意
("现在的任务状态:"+());
("情况属实,可以批准",null);//小亮审批同意
();
("----整体审批路径如下----");
();
}
运行结果:
---------驳回流程--------
新建任务
现在的任务状态:状态名:提交状态 状态id:0 下一环节id:-1 操作用户:小红 是否进行了操作:false 是否同意:false 申请信息:null 意见信息:null
提交环节同意
现在的任务状态:状态名:一级审批 状态id:1 下一环节id:-1 操作用户:小明 是否进行了操作:false 是否同意:false 申请信息:由于工作需要,申请对工程机进行使用 3天 意见信息:null
一级审批同意
现在的任务状态:状态名:二级审批 状态id:2 下一环节id:-1 操作用户:小亮 是否进行了操作:false 是否同意:false 申请信息:小红需要对工程师进行使用,初步决定可以 意见信息:null
二级审批回退
现在的任务状态:状态名:一级审批 状态id:1 下一环节id:-1 操作用户:小明 是否进行了操作:false 是否同意:false 申请信息:不行呀,请再检查检查 意见信息:null
一级审批同意
现在的任务状态:状态名:二级审批 状态id:2 下一环节id:-1 操作用户:小亮 是否进行了操作:false 是否同意:false 申请信息:已经检查过库存了,确定可以 意见信息:null
二级审批同意
环节结束 是否异常:true
----整体审批路径如下----
状态名:提交状态 状态id:0 下一环节id:1 操作用户:小红 是否进行了操作:true 是否同意:true 申请信息:null 意见信息:由于工作需要,申请对工程机进行使用 3天
状态名:一级审批 状态id:1 下一环节id:2 操作用户:小明 是否进行了操作:true 是否同意:true 申请信息:由于工作需要,申请对工程机进行使用 3天 意见信息:小红需要对工程师进行使用,初步决定可以
状态名:二级审批 状态id:2 下一环节id:1 操作用户:小亮 是否进行了操作:true 是否同意:false 申请信息:小红需要对工程师进行使用,初步决定可以 意见信息:不行呀,请再检查检查
状态名:一级审批 状态id:1 下一环节id:2 操作用户:小明 是否进行了操作:true 是否同意:true 申请信息:不行呀,请再检查检查 意见信息:已经检查过库存了,确定可以
状态名:二级审批 状态id:2 下一环节id:3 操作用户:小亮 是否进行了操作:true 是否同意:true 申请信息:已经检查过库存了,确定可以 意见信息:情况属实,可以批准
状态名:结束环境 状态id:-1 下一环节id:-1 操作用户:小亮 是否进行了操作:true 是否同意:true 申请信息:情况属实,可以批准 意见信息:null
小结:
以上就是状态模式模拟审批流程的简单例子。
当然,有很多优化的地方,比如 驳回可以选择驳回环节、任务类需要可以进行序列化和反序列化,可以把任务结果保存入数据库中,并且可以根据数据库中存入的值还原成任务类等操作。