設計模式的狀態模式,就是把狀態獨立成一個類,代替傳統複雜的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) {
this.user = user;
this.request = request;
}
/**
* 結束
* @param task 任務
* @param msg 意見
*/
public boolean finish(ApproveTask task,String msg,boolean agreeFlag){
System.out.println("環節結束 是否異常:"+agreeFlag);
task.getApproveStatusList().add(this);//放入審批鏈中
this.suggest = msg;
this.agreeFlag = agreeFlag;
this.nextStatusId = 3;
this.operationFlag = true;
//異常終止
FinishStatus finishStatus = new FinishStatus(this.user,msg,agreeFlag);
task.setStatus(finishStatus);
task.getApproveStatusList().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);
this.statusName = "提交狀態";
this.statusId = 0;
}
@Override
public boolean agreeToNext(ApproveTask task,String msg,String nextUser){
System.out.println("提交環節同意");
this.operationFlag = true;
this.agreeFlag = true;//
this.suggest = msg;
this.nextStatusId = statusId + 1;//下級狀態
task.getApproveStatusList().add(this);//放入審批路徑
task.setStatus(new FirstApproveStatus(nextUser,suggest));//設置下級審批,當前的意見成爲下一審批人的申請信息
return true;
}
@Override
public boolean backToBefore(ApproveTask task,String msg){
System.out.println("提交環節,不能打回");
return false;
}
}
一級審批狀態類:
/**
* 提交狀態0->一級審批1->二級審批2 ->審批完成3
* 一級審批
*/
public class FirstApproveStatus extends Status{
/**
* 構造方法
*
* @param user
*/
public FirstApproveStatus(String user,String request) {
super(user,request);
this.statusName = "一級審批";
this.statusId = 1;
}
@Override
public boolean agreeToNext(ApproveTask task, String msg, String nextUser) {
System.out.println("一級審批同意");
this.agreeFlag = true;//
this.operationFlag = true;
this.suggest = msg;
this.nextStatusId = statusId + 1;//下級狀態
task.getApproveStatusList().add(this);//放入審批路徑
task.setStatus(new SecondApproveStatus(nextUser,this.suggest));//設置下級審批
return true;
}
@Override
public boolean backToBefore(ApproveTask task, String msg) {
System.out.println("一級審批迴退");
this.agreeFlag = false;
this.operationFlag = true;
this.suggest = msg;
this.nextStatusId = statusId -1;
String beforeUserId = task.getApproveStatusList().get(task.getApproveStatusList().size() - 1).user;
task.getApproveStatusList().add(this);
task.setStatus(new SubmitStatus(beforeUserId,this.suggest));
return true;
}
}
二級審批狀態類:
public class SecondApproveStatus extends Status {
/**
* 構造方法
*
* @param user
*/
public SecondApproveStatus(String user,String request) {
super(user,request);
this.statusName = "二級審批";
this.statusId = 2;
}
@Override
public boolean agreeToNext(ApproveTask task, String msg, String nextUser) {
System.out.println("二級審批同意");
/* this.agreeFlag = true;//
this.suggest = msg;
this.nextStatusId = statusId + 1;//下級狀態
task.getApproveStatusList().add(this);//放入審批路徑
Status finishStatus = new FinishStatus(this.user,this.suggest,true);
task.setStatus(finishStatus);//設置下級審批
task.getApproveStatusList().add(finishStatus);//放入審批路徑
*/
this.finish(task,msg,true);
return true;
}
@Override
public boolean backToBefore(ApproveTask task, String msg) {
System.out.println("二級審批迴退");
this.agreeFlag = false;
this.suggest = msg;
this.nextStatusId = statusId -1;
this.operationFlag = true;
String beforeUserId = task.getApproveStatusList().get(task.getApproveStatusList().size() - 1).user;
task.getApproveStatusList().add(this);
task.setStatus(new FirstApproveStatus(beforeUserId,this.suggest));
return true;
}
}
結束狀態類:
/**
* 提交狀態0->一級審批1->二級審批2 ->審批完成3
* 一級審批
*/
public class FirstApproveStatus extends Status{
/**
* 構造方法
*
* @param user
*/
public FirstApproveStatus(String user,String request) {
super(user,request);
this.statusName = "一級審批";
this.statusId = 1;
}
@Override
public boolean agreeToNext(ApproveTask task, String msg, String nextUser) {
System.out.println("一級審批同意");
this.agreeFlag = true;//
this.operationFlag = true;
this.suggest = msg;
this.nextStatusId = statusId + 1;//下級狀態
task.getApproveStatusList().add(this);//放入審批路徑
task.setStatus(new SecondApproveStatus(nextUser,this.suggest));//設置下級審批
return true;
}
@Override
public boolean backToBefore(ApproveTask task, String msg) {
System.out.println("一級審批迴退");
this.agreeFlag = false;
this.operationFlag = true;
this.suggest = msg;
this.nextStatusId = statusId -1;
String beforeUserId = task.getApproveStatusList().get(task.getApproveStatusList().size() - 1).user;
task.getApproveStatusList().add(this);
task.setStatus(new SubmitStatus(beforeUserId,this.suggest));
return true;
}
}
接下來是任務類,也就是很多網絡例子的Context上下文 或環境。
這個類有當前的狀態、還有整個審批狀態流的屬性,還有提供對外的 同意、駁回、拒絕等操作,外部只需要對此類進行操作即可。
import java.util.ArrayList;
import java.util.List;
/**
* 審批任務 相當於 狀態模式的Context 環境
*
*/
public class ApproveTask {
private Status status;//當前的狀態
private List<Status> approveStatusList = new ArrayList<>();//審批操作人 此處可以擴展
/**
* 任務 構造函數
*/
public ApproveTask(String user){
this.status = new SubmitStatus(user,null);
}
/**
* 同意 要寫同意意見
* @param msg 同意意見
*/
public void agree(String msg,String nextUser){
status.agreeToNext(this,msg,nextUser);
}
/**
* 駁回
* @param msg
*/
public void back(String msg){
status.backToBefore(this,msg);
}
public void refuse(String msg){
status.finish(this,msg,false);
}
/**
* 打印審批路徑
*/
public void showApprovePath(){
approveStatusList.stream().forEach(System.out::println);
}
//getter and setter
public Status getStatus() {
return status;
}
public void setStatus(Status status) {
this.status = status;
}
public List<Status> getApproveStatusList() {
return approveStatusList;
}
public void setApproveStatusList(List<Status> approveStatusList) {
this.approveStatusList = approveStatusList;
}
}
測試方法1(正常二級審批流程):
public static void normal(){
System.out.println("---------正常流程----------");
ApproveTask approveTask = new ApproveTask("小紅");//小紅新建個審批任務
approveTask.agree("由於工作需要,申請對工程機進行使用 3天","小明");//小紅寫申請書,提交給 小明
approveTask.agree("小紅需要對工程師進行使用,初步決定可以","小亮");//小明一級審批同意,併發送給小亮
approveTask.agree("情況屬實,可以批准",null);//小亮審批同意
System.out.println();
System.out.println("----整體審批路徑如下----");
approveTask.showApprovePath();
}
運行結果:
---------正常流程----------
提交環節同意
一級審批同意
二級審批同意
環節結束 是否異常: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(){
System.out.println("---------駁回流程--------");
System.out.println("新建任務");
ApproveTask approveTask = new ApproveTask("小紅");//小紅新建個審批任務
System.out.println("現在的任務狀態:"+approveTask.getStatus());
approveTask.agree("由於工作需要,申請對工程機進行使用 3天","小明");//小紅寫申請書,提交給 小明
System.out.println("現在的任務狀態:"+approveTask.getStatus());
approveTask.agree("小紅需要對工程師進行使用,初步決定可以","小亮");//小明一級審批同意,併發送給小亮
System.out.println("現在的任務狀態:"+approveTask.getStatus());
approveTask.back("不行呀,請再檢查檢查");//小亮 二級審批不同意
System.out.println("現在的任務狀態:"+approveTask.getStatus());
approveTask.agree("已經檢查過庫存了,確定可以","小亮");//小明一級審批再次同意
System.out.println("現在的任務狀態:"+approveTask.getStatus());
approveTask.agree("情況屬實,可以批准",null);//小亮審批同意
System.out.println();
System.out.println("----整體審批路徑如下----");
approveTask.showApprovePath();
}
運行結果:
---------駁回流程--------
新建任務
現在的任務狀態:狀態名:提交狀態 狀態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
小結:
以上就是狀態模式模擬審批流程的簡單例子。
當然,有很多優化的地方,比如 駁回可以選擇駁回環節、任務類需要可以進行序列化和反序列化,可以把任務結果保存入數據庫中,並且可以根據數據庫中存入的值還原成任務類等操作。