3.4 模擬工作流##
做企業(yè)應(yīng)用的朋友饥脑,大多數(shù)都接觸過(guò)工作流言疗,至少處理過(guò)業(yè)務(wù)流程晴圾。當(dāng)然對(duì)于工作流,復(fù)雜的應(yīng)用可能會(huì)使用工作流中間件噪奄,用工作流引擎來(lái)負(fù)責(zé)流程處理死姚,這個(gè)會(huì)比較復(fù)雜,其實(shí)工作流引擎的實(shí)現(xiàn)也可以應(yīng)用上狀態(tài)模式
勤篮,這里不去討論都毒。
簡(jiǎn)單點(diǎn)的,把流程數(shù)據(jù)存放在數(shù)據(jù)庫(kù)里面碰缔,然后在程序里面自己來(lái)進(jìn)行流程控制账劲。對(duì)于簡(jiǎn)單點(diǎn)的業(yè)務(wù)流程控制,可以使用狀態(tài)模式來(lái)輔助進(jìn)行流程控制金抡,因?yàn)榇蟛糠诌@種流程都是狀態(tài)驅(qū)動(dòng)的
瀑焦。
舉個(gè)例子來(lái)說(shuō)明吧,舉個(gè)最常見(jiàn)的“請(qǐng)假流程”梗肝,流程是這樣的:當(dāng)某人提出請(qǐng)假申請(qǐng)過(guò)后榛瓮,先由項(xiàng)目經(jīng)理來(lái)審批,如果項(xiàng)目經(jīng)理不同意统捶,審批就直接結(jié)束榆芦;如果項(xiàng)目經(jīng)理同意了,再看請(qǐng)假的天數(shù)是否超過(guò)3天喘鸟,項(xiàng)目經(jīng)理的審批權(quán)限只有3天以內(nèi),如果請(qǐng)假天數(shù)在3天以內(nèi)驻右,那么審批也直接結(jié)束什黑,否則就提交給部門經(jīng)理;部門經(jīng)理審核過(guò)后堪夭,無(wú)論是否同意愕把,審批都直接結(jié)束。流程圖如圖所示:
在實(shí)際開(kāi)發(fā)中森爽,如果不考慮使用工作流軟件恨豁,按照流程來(lái)自己實(shí)現(xiàn)的話,這個(gè)流程基本的運(yùn)行過(guò)程簡(jiǎn)化描述如下:
- UI操作:請(qǐng)假人填寫請(qǐng)假單爬迟,提出請(qǐng)假申請(qǐng)
- 后臺(tái)處理:保存請(qǐng)假單數(shù)據(jù)到數(shù)據(jù)庫(kù)中橘蜜,然后為項(xiàng)目經(jīng)理創(chuàng)建一個(gè)工作,把工作信息保存到數(shù)據(jù)庫(kù)中
- UI操作:項(xiàng)目經(jīng)理登錄系統(tǒng),獲取自己的工作列表
- 后臺(tái)處理:從數(shù)據(jù)庫(kù)中獲取相應(yīng)的工作列表
- UI操作:項(xiàng)目經(jīng)理完成審核工作计福,提交保存
- 后臺(tái)處理:處理項(xiàng)目經(jīng)理審核的業(yè)務(wù)跌捆,保存審核的信息到數(shù)據(jù)庫(kù)。同時(shí)判斷后續(xù)的工作象颖,如果是需要人員參與的佩厚,就為參與下一個(gè)工作的人員創(chuàng)建工作,把工作信息保存到數(shù)據(jù)庫(kù)中
- UI操作:部門經(jīng)理登錄系統(tǒng)说订,獲取自己的工作列表抄瓦,基本上是重復(fù)第3步
- 后臺(tái)處理:從數(shù)據(jù)庫(kù)中獲取相應(yīng)的工作列表,基本上是重復(fù)第4步
- UI操作:部門經(jīng)理完成審核工作陶冷,提交保存钙姊,基本上是重復(fù)第5步
- 后臺(tái)處理:類推,基本上是重復(fù)第6步
- 實(shí)現(xiàn)思路
仔細(xì)分析上面的流程圖和運(yùn)行過(guò)程埃叭,把請(qǐng)假單在流程中的各個(gè)階段的狀態(tài)分析出來(lái)摸恍,會(huì)發(fā)現(xiàn),整個(gè)流程完全可以看成是狀態(tài)驅(qū)動(dòng)的
赤屋。
在上面的流程中立镶,請(qǐng)假單大致有如下?tīng)顟B(tài):等待項(xiàng)目經(jīng)理審核、等待部門經(jīng)理審核类早、審核結(jié)束媚媒。如果用狀態(tài)驅(qū)動(dòng)來(lái)描述上述流程:
當(dāng)請(qǐng)假人填寫請(qǐng)假單,提出請(qǐng)假申請(qǐng)后涩僻,請(qǐng)假單的狀態(tài)是等待項(xiàng)目經(jīng)理審核狀態(tài)缭召;
當(dāng)項(xiàng)目經(jīng)理完成審核工作,提交保存后逆日,如果項(xiàng)目經(jīng)理不同意嵌巷,請(qǐng)假單的狀態(tài)是審核結(jié)束狀態(tài);如果項(xiàng)目經(jīng)理同意室抽,請(qǐng)假天數(shù)又在3天以內(nèi)搪哪,請(qǐng)假單的狀態(tài)是審核結(jié)束狀態(tài);如果項(xiàng)目經(jīng)理同意坪圾,請(qǐng)假天數(shù)大于3天晓折,請(qǐng)假單的狀態(tài)是等待部門經(jīng)理審核狀態(tài);
當(dāng)部門經(jīng)理完成審核工作兽泄,提交保存后漓概,無(wú)論是否同意,請(qǐng)假單的狀態(tài)都是審核結(jié)束狀態(tài)病梢;
既然可以把流程看成是狀態(tài)驅(qū)動(dòng)的胃珍,那么就可以自然的使用上狀態(tài)模式,每次當(dāng)相應(yīng)的工作人員完成工作,請(qǐng)求流程響應(yīng)的時(shí)候堂鲜,流程處理的對(duì)象會(huì)根據(jù)當(dāng)前所處的狀態(tài)栈雳,把流程處理委托給相應(yīng)的狀態(tài)對(duì)象去處理
。
又考慮到在一個(gè)系統(tǒng)中會(huì)有很多流程缔莲,雖然不像通用工作流那么復(fù)雜的設(shè)計(jì)哥纫,但還是稍稍提煉一下,至少把各個(gè)不同的業(yè)務(wù)流程痴奏,在應(yīng)用狀態(tài)模式時(shí)的公共功能蛀骇,或者是架子給搭出來(lái),以便復(fù)用這些功能读拆。
(1)首先提供一個(gè)公共的狀態(tài)處理機(jī)
相當(dāng)于一個(gè)公共的狀態(tài)模式的Context擅憔,在里面提供基本的、公共的功能檐晕,這樣在實(shí)現(xiàn)具體的流程的時(shí)候暑诸,可以簡(jiǎn)單一些,對(duì)于要求不復(fù)雜的流程辟灰,甚至可以直接使用个榕。示例代碼如下:
/**
* 公共狀態(tài)處理機(jī),相當(dāng)于狀態(tài)模式的Context
* 包含所有流程使用狀態(tài)模式時(shí)的公共功能
*/
public class StateMachine {
/**
* 持有一個(gè)狀態(tài)對(duì)象
*/
private State state = null;
/**
* 包含流程處理需要的業(yè)務(wù)數(shù)據(jù)對(duì)象芥喇,不知道具體類型,為了簡(jiǎn)單西采,不去使用泛型,
* 用Object继控,反正只是傳遞到具體的狀態(tài)對(duì)象里面
*/
private Object businessVO = null;
/**
* 執(zhí)行工作械馆,客戶端處理流程的接口方法。
* 在客戶完成自己的業(yè)務(wù)工作后調(diào)用
*/
public void doWork(){
//轉(zhuǎn)調(diào)相應(yīng)的狀態(tài)對(duì)象真正完成功能處理
this.state.doWork(this);
}
public State getState() {
return state;
}
public void setState(State state) {
this.state = state;
}
public Object getBusinessVO() {
return businessVO;
}
public void setBusinessVO(Object businessVO) {
this.businessVO = businessVO;
}
}
(2)來(lái)提供公共的狀態(tài)接口武通,各個(gè)狀態(tài)對(duì)象在處理流程的時(shí)候霹崎,可以使用統(tǒng)一的接口,那么它們需要的業(yè)務(wù)數(shù)據(jù)從何而來(lái)呢冶忱?那就通過(guò)上下文傳遞過(guò)來(lái)仿畸。示例代碼如下:
/**
* 公共狀態(tài)接口
*/
public interface State {
/**
* 執(zhí)行狀態(tài)對(duì)應(yīng)的功能處理
* @param ctx 上下文的實(shí)例對(duì)象
*/
public void doWork(StateMachine ctx);
}
好了,現(xiàn)在架子已經(jīng)搭出來(lái)了朗和,在實(shí)現(xiàn)具體的流程的時(shí)候,可以分別擴(kuò)展它們簿晓,來(lái)加入跟具體流程相關(guān)的功能眶拉。
- 使用狀態(tài)模式來(lái)實(shí)現(xiàn)流程
(1)定義請(qǐng)假單的業(yè)務(wù)數(shù)據(jù)模型,示例代碼如下:
public class LeaveRequestModel {
/**
* 請(qǐng)假人
*/
private String user;
/**
* 請(qǐng)假開(kāi)始時(shí)間
*/
private String beginDate;
/**
* 請(qǐng)假天數(shù)
*/
private int leaveDays;
/**
* 審核結(jié)果
*/
private String result;
public String getResult() {
return result;
}
public void setResult(String result) {
this.result = result;
}
public String getUser() {
return user;
}
public String getBeginDate() {
return beginDate;
}
public int getLeaveDays() {
return leaveDays;
}
public void setUser(String user) {
this.user = user;
}
public void setBeginDate(String beginDate) {
this.beginDate = beginDate;
}
public void setLeaveDays(int leaveDays) {
this.leaveDays = leaveDays;
}
}
(2)定義處理客戶端請(qǐng)求的上下文憔儿,雖然這里并不需要擴(kuò)展功能忆植,但還是繼承一下?tīng)顟B(tài)機(jī),表示可以添加自己的處理。示例代碼如下:
public class LeaveRequestContext extends StateMachine{
//這里可以擴(kuò)展跟自己流程相關(guān)的處理
}
(3)來(lái)定義處理請(qǐng)假流程的狀態(tài)接口朝刊,雖然這里并不需要擴(kuò)展功能耀里,但還是繼承一下?tīng)顟B(tài),表示可以添加自己的處理拾氓。示例代碼如下:
public interface LeaveRequestState extends State{
//這里可以擴(kuò)展跟自己流程相關(guān)的處理
}
(4)接下來(lái)該來(lái)實(shí)現(xiàn)各個(gè)狀態(tài)具體的處理對(duì)象了冯挎,先看看處理項(xiàng)目經(jīng)理審核的狀態(tài)類的實(shí)現(xiàn),示例代碼如下:
/**
* 處理項(xiàng)目經(jīng)理的審核咙鞍,處理后可能對(duì)應(yīng)部門經(jīng)理審核房官、審核結(jié)束之中的一種
*/
public class ProjectManagerState implements LeaveRequestState{
public void doWork(StateMachine request) {
//先把業(yè)務(wù)對(duì)象造型回來(lái)
LeaveRequestModel lrm = (LeaveRequestModel)request.getBusinessVO();
//業(yè)務(wù)處理,把審核結(jié)果保存到數(shù)據(jù)庫(kù)中
//根據(jù)選擇的結(jié)果和條件來(lái)設(shè)置下一步
if("同意".equals(lrm.getResult())){
if(lrm.getLeaveDays() > 3){
//如果請(qǐng)假天數(shù)大于3天续滋,而且項(xiàng)目經(jīng)理同意了翰守,就提交給部門經(jīng)理
request.setState(new DepManagerState());
//為部門經(jīng)理增加一個(gè)工作
}else{
//3天以內(nèi)的請(qǐng)假,由項(xiàng)目經(jīng)理做主,
//就不用提交給部門經(jīng)理了疲酌,轉(zhuǎn)向?qū)徍私Y(jié)束狀態(tài)
request.setState(new AuditOverState());
//給申請(qǐng)人增加一個(gè)工作蜡峰,讓他查看審核結(jié)果
}
}else{
//項(xiàng)目經(jīng)理不同意的話,也就不用提交給部門經(jīng)理了朗恳,轉(zhuǎn)向?qū)徍私Y(jié)束狀態(tài)
request.setState(new AuditOverState());
//給申請(qǐng)人增加一個(gè)工作湿颅,讓他查看審核結(jié)果
}
}
}
接下來(lái)看看處理項(xiàng)目經(jīng)理審核的狀態(tài)類的實(shí)現(xiàn),示例代碼如下:
/**
* 處理部門經(jīng)理的審核僻肖,處理后對(duì)應(yīng)審核結(jié)束狀態(tài)
*/
public class DepManagerState implements LeaveRequestState{
public void doWork(StateMachine request) {
//先把業(yè)務(wù)對(duì)象造型回來(lái)
LeaveRequestModel lrm = (LeaveRequestModel)request.getBusinessVO();
//業(yè)務(wù)處理肖爵,把審核結(jié)果保存到數(shù)據(jù)庫(kù)中
//部門經(jīng)理審核過(guò)后,直接轉(zhuǎn)向?qū)徍私Y(jié)束狀態(tài)了
request.setState(new AuditOverState());
//給申請(qǐng)人增加一個(gè)工作臀脏,讓他查看審核結(jié)果
}
}
再來(lái)看看處理審核結(jié)束的狀態(tài)類的實(shí)現(xiàn)劝堪,示例代碼如下:
/**
* 處理審核結(jié)束的類
*/
public class AuditOverState implements LeaveRequestState{
public void doWork(StateMachine request) {
//先把業(yè)務(wù)對(duì)象造型回來(lái)
LeaveRequestModel lrm = (LeaveRequestModel)request.getBusinessVO();
//業(yè)務(wù)處理,在數(shù)據(jù)里面記錄整個(gè)流程結(jié)束
}
}
(5)由于上面的實(shí)現(xiàn)中揉稚,涉及到大量需要數(shù)據(jù)庫(kù)支持的功能秒啦,同時(shí)還需要提供頁(yè)面來(lái)讓用戶操作,才能驅(qū)動(dòng)流程運(yùn)行搀玖,所以無(wú)法像其它示例那樣余境,寫個(gè)客戶端就能進(jìn)行測(cè)試。當(dāng)然這個(gè)可以在后面稍稍改變一下灌诅,模擬一下實(shí)現(xiàn)芳来,就可以運(yùn)行起來(lái)看效果了。
先來(lái)看看此時(shí)用狀態(tài)模式實(shí)現(xiàn)的這個(gè)流程的程序結(jié)構(gòu)示意圖猜拾,如圖所示:
- 改進(jìn)上面使用狀態(tài)模式來(lái)實(shí)現(xiàn)流程的示例
上面的示例不能運(yùn)行有兩個(gè)基本原因:一是沒(méi)有數(shù)據(jù)庫(kù)實(shí)現(xiàn)部分即舌,二是沒(méi)有界面
。要解決這個(gè)問(wèn)題挎袜,那就采用字符界面顽聂,來(lái)讓客戶輸入數(shù)據(jù)肥惭,另外把運(yùn)行放到同一個(gè)線程里面,這樣就不存在傳遞數(shù)據(jù)的問(wèn)題紊搪,也就不需要保存數(shù)據(jù)了蜜葱,數(shù)據(jù)在內(nèi)存里面
。
原來(lái)是提交了請(qǐng)假申請(qǐng)耀石,把數(shù)據(jù)保存在數(shù)據(jù)庫(kù)里面牵囤,然后項(xiàng)目經(jīng)理從數(shù)據(jù)庫(kù)去獲取這些數(shù)據(jù)。現(xiàn)在一步到位娶牌,直接把申請(qǐng)數(shù)據(jù)傳遞過(guò)去奔浅,就可以處理了。
(1)根據(jù)上面的思路诗良,其實(shí)也就只是需要修改那幾個(gè)狀態(tài)處理對(duì)象的實(shí)現(xiàn)汹桦,先看看處理項(xiàng)目經(jīng)理審核的狀態(tài)類的實(shí)現(xiàn),使用Scanner來(lái)接受命令行輸入數(shù)據(jù)鉴裹,示例代碼如下:
import java.util.Scanner;
/**
* 處理項(xiàng)目經(jīng)理的審核舞骆,處理后可能對(duì)應(yīng)部門經(jīng)理審核、審核結(jié)束之中的一種
*/
public class ProjectManagerState implements LeaveRequestState{
public void doWork(StateMachine request) {
//先把業(yè)務(wù)對(duì)象造型回來(lái)
LeaveRequestModel lrm = (LeaveRequestModel)request.getBusinessVO();
System.out.println("項(xiàng)目經(jīng)理審核中径荔,請(qǐng)稍候......");
//模擬用戶處理界面督禽,通過(guò)控制臺(tái)來(lái)讀取數(shù)據(jù)
System.out.println(lrm.getUser()+"申請(qǐng)從"+lrm.getBeginDate()+"開(kāi)始請(qǐng)假"+lrm.getLeaveDays()+"天,請(qǐng)項(xiàng)目經(jīng)理審核(1為同意,2為不同意):");
//讀取從控制臺(tái)輸入的數(shù)據(jù)
Scanner scanner = new Scanner(System.in);
if(scanner.hasNext()){
int a = scanner.nextInt();
//設(shè)置回到上下文中
String result = "不同意";
if(a==1){
result = "同意";
}
lrm.setResult("項(xiàng)目經(jīng)理審核結(jié)果:"+result);
//根據(jù)選擇的結(jié)果和條件來(lái)設(shè)置下一步
if(a==1){
if(lrm.getLeaveDays() > 3){
//如果請(qǐng)假天數(shù)大于3天总处,而且項(xiàng)目經(jīng)理同意了狈惫,
//就提交給部門經(jīng)理
request.setState(new DepManagerState());
//繼續(xù)執(zhí)行下一步工作
request.doWork();
}else{
//3天以內(nèi)的請(qǐng)假,由項(xiàng)目經(jīng)理做主,就不用提交給部門經(jīng)理了鹦马,
//轉(zhuǎn)向?qū)徍私Y(jié)束狀態(tài)
request.setState(new AuditOverState());
//繼續(xù)執(zhí)行下一步工作
request.doWork();
}
}else{
//項(xiàng)目經(jīng)理不同意胧谈,就不用提交給部門經(jīng)理了,轉(zhuǎn)向?qū)徍私Y(jié)束狀態(tài)
request.setState(new AuditOverState());
//繼續(xù)執(zhí)行下一步工作
request.doWork();
}
}
}
}
接下來(lái)看看處理項(xiàng)目經(jīng)理審核的狀態(tài)類的實(shí)現(xiàn)荸频,示例代碼如下:
import java.util.Scanner;
/**
* 處理部門經(jīng)理的審核菱肖,處理后對(duì)應(yīng)審核結(jié)束狀態(tài)
*/
public class DepManagerState implements LeaveRequestState{
public void doWork(StateMachine request) {
//先把業(yè)務(wù)對(duì)象造型回來(lái)
LeaveRequestModel lrm = (LeaveRequestModel)request.getBusinessVO();
System.out.println("部門經(jīng)理審核中,請(qǐng)稍候......");
//模擬用戶處理界面旭从,通過(guò)控制臺(tái)來(lái)讀取數(shù)據(jù)
System.out.println(lrm.getUser()+"申請(qǐng)從"+lrm.getBeginDate()+"開(kāi)始請(qǐng)假"+lrm.getLeaveDays()+"天,請(qǐng)部門經(jīng)理審核(1為同意稳强,2為不同意):");
//讀取從控制臺(tái)輸入的數(shù)據(jù)
Scanner scanner = new Scanner(System.in);
if(scanner.hasNext()){
int a = scanner.nextInt();
//設(shè)置回到上下文中
String result = "不同意";
if(a==1){
result = "同意";
}
lrm.setResult("部門經(jīng)理審核結(jié)果:"+result);
//部門經(jīng)理審核過(guò)后,直接轉(zhuǎn)向?qū)徍私Y(jié)束狀態(tài)了
request.setState(new AuditOverState());
//繼續(xù)執(zhí)行下一步工作
request.doWork();
}
}
}
再來(lái)看看處理審核結(jié)束的狀態(tài)類的實(shí)現(xiàn)和悦,示例代碼如下:
public class AuditOverState implements LeaveRequestState{
public void doWork(StateMachine request) {
//先把業(yè)務(wù)對(duì)象造型回來(lái)
LeaveRequestModel lrm = (LeaveRequestModel)request.getBusinessVO();
System.out.println(lrm.getUser()+"退疫,你的請(qǐng)假申請(qǐng)已經(jīng)審核結(jié)束,結(jié)果是:"+lrm.getResult());
}
}
(2)萬(wàn)事俱備鸽素,可以寫個(gè)客戶端蹄咖,來(lái)開(kāi)始我們的流程之旅了。示例代碼如下:
public class Client {
public static void main(String[] args) {
//創(chuàng)建業(yè)務(wù)對(duì)象付鹿,并設(shè)置業(yè)務(wù)數(shù)據(jù)
LeaveRequestModel lrm = new LeaveRequestModel();
lrm.setUser("小李");
lrm.setBeginDate("2010-02-08");
lrm.setLeaveDays(5);
//創(chuàng)建上下文對(duì)象
LeaveRequestContext request = new LeaveRequestContext();
//為上下文對(duì)象設(shè)置業(yè)務(wù)數(shù)據(jù)對(duì)象
request.setBusinessVO(lrm);
//配置上下文澜汤,作為開(kāi)始的狀態(tài),以后就不管了
request.setState(new ProjectManagerState());
//請(qǐng)求上下文舵匾,讓上下文開(kāi)始處理工作
request.doWork();
}
}
辛苦了這么久俊抵,一定要好好的運(yùn)行一下,體會(huì)在流程處理中是如何使用狀態(tài)模式的坐梯。
第一步:運(yùn)行一下徽诲,剛開(kāi)始會(huì)出現(xiàn)如下信息:
項(xiàng)目經(jīng)理審核中,請(qǐng)稍候......
小李申請(qǐng)從2010-02-08開(kāi)始請(qǐng)假5天,請(qǐng)項(xiàng)目經(jīng)理審核(1為同意吵血,2為不同意):
第二步:程序并沒(méi)有停止谎替,在等待你輸入項(xiàng)目經(jīng)理審核的結(jié)果,如果你輸入1蹋辅,表示同意钱贯,那么程序會(huì)繼續(xù)判斷,發(fā)現(xiàn)請(qǐng)假天數(shù)5天大于項(xiàng)目經(jīng)理審核的范圍了侦另,會(huì)提交給部門經(jīng)理審核秩命。在控制臺(tái)輸入1,然后回車看看褒傅,會(huì)出現(xiàn)如下信息:
項(xiàng)目經(jīng)理審核中弃锐,請(qǐng)稍候......
小李申請(qǐng)從2010-02-08開(kāi)始請(qǐng)假5天,請(qǐng)項(xiàng)目經(jīng)理審核(1為同意,2為不同意):
1
部門經(jīng)理審核中殿托,請(qǐng)稍候......
小李申請(qǐng)從2010-02-08開(kāi)始請(qǐng)假5天,請(qǐng)部門經(jīng)理審核(1為同意霹菊,2為不同意):
第三步:同樣,程序仍然沒(méi)有停止支竹,在等待你輸入部門經(jīng)理審核的結(jié)果旋廷,假如輸入1,然后回車唾戚,看看會(huì)發(fā)生什么柳洋,提示信息如下:
項(xiàng)目經(jīng)理審核中,請(qǐng)稍候......
小李申請(qǐng)從2010-02-08開(kāi)始請(qǐng)假5天,請(qǐng)項(xiàng)目經(jīng)理審核(1為同意叹坦,2為不同意):
1
部門經(jīng)理審核中熊镣,請(qǐng)稍候......
小李申請(qǐng)從2010-02-08開(kāi)始請(qǐng)假5天,請(qǐng)部門經(jīng)理審核(1為同意,2為不同意):
1
小李募书,你的請(qǐng)假申請(qǐng)已經(jīng)審核結(jié)束绪囱,結(jié)果是:部門經(jīng)理審核結(jié)果:同意
這個(gè)時(shí)候流程運(yùn)行結(jié)束了,程序運(yùn)行也結(jié)束了莹捡,有點(diǎn)流程控制的意味了吧鬼吵。
如果在上面第一步運(yùn)行過(guò)后,在第二步輸入2篮赢,也就是項(xiàng)目經(jīng)理不同意齿椅,會(huì)怎樣呢琉挖?應(yīng)該就不會(huì)再到部門經(jīng)理了吧,試試看涣脚,運(yùn)行提示信息如下:
項(xiàng)目經(jīng)理審核中示辈,請(qǐng)稍候......
小李申請(qǐng)從2010-02-08開(kāi)始請(qǐng)假5天,請(qǐng)項(xiàng)目經(jīng)理審核(1為同意,2為不同意):
2
小李遣蚀,你的請(qǐng)假申請(qǐng)已經(jīng)審核結(jié)束矾麻,結(jié)果是:項(xiàng)目經(jīng)理審核結(jié)果:不同意
- 小結(jié)一下
事實(shí)上,上面的程序可以和數(shù)據(jù)庫(kù)結(jié)合起來(lái)芭梯,比如把審核結(jié)果存放在數(shù)據(jù)庫(kù)里面险耀,也可以把審核的步驟也放到數(shù)據(jù)庫(kù)里面,每次運(yùn)行的時(shí)候從數(shù)據(jù)庫(kù)里面獲取這些值玖喘,然后來(lái)判斷是創(chuàng)建哪一個(gè)狀態(tài)處理類甩牺,然后執(zhí)行相應(yīng)的處理就可以了。
現(xiàn)在這些東西都在內(nèi)存里芒涡,所以程序不能停止柴灯,否則流程就運(yùn)行不下去了。
另外费尽,為了演示的簡(jiǎn)潔性赠群,這里做了相當(dāng)?shù)暮?jiǎn)化,比如沒(méi)有去根據(jù)申請(qǐng)人選擇相應(yīng)的項(xiàng)目經(jīng)理和部門經(jīng)理旱幼,也沒(méi)有去考慮如果申請(qǐng)人就是項(xiàng)目經(jīng)理或者部門經(jīng)理怎么辦查描,只是為了讓大家看明白狀態(tài)模式在這里面的應(yīng)用,主要是為了體現(xiàn)狀態(tài)模式而不是業(yè)務(wù)柏卤。
3.5 狀態(tài)模式的優(yōu)缺點(diǎn)##
- 簡(jiǎn)化應(yīng)用邏輯控制
狀態(tài)模式使用單獨(dú)的類來(lái)封裝一個(gè)狀態(tài)的處理
冬三。如果把一個(gè)大的程序控制分成很多小塊,每塊定義一個(gè)狀態(tài)來(lái)代表缘缚,那么就可以把這些邏輯控制的代碼分散到很多單獨(dú)的狀態(tài)類當(dāng)中去勾笆,這樣就把著眼點(diǎn)從執(zhí)行狀態(tài)提高到整個(gè)對(duì)象的狀態(tài),使得代碼結(jié)構(gòu)化和意圖更清晰桥滨,從而簡(jiǎn)化應(yīng)用的邏輯控制窝爪。
對(duì)于依賴于狀態(tài)的if-else,理論上來(lái)講齐媒,也可以改變成應(yīng)用狀態(tài)模式來(lái)實(shí)現(xiàn)蒲每,把每個(gè)if或else塊定義一個(gè)狀態(tài)來(lái)代表,那么就可以把塊內(nèi)的功能代碼移動(dòng)到狀態(tài)處理類去了喻括,從而減少if-else邀杏,避免出現(xiàn)巨大的條件語(yǔ)句
。
- 更好的分離狀態(tài)和行為
狀態(tài)模式通過(guò)設(shè)置所有狀態(tài)類的公共接口唬血,把狀態(tài)和狀態(tài)對(duì)應(yīng)的行為分離開(kāi)來(lái)望蜡,把所有與一個(gè)特定的狀態(tài)相關(guān)的行為都放入一個(gè)對(duì)象中
唤崭,使得應(yīng)用程序在控制的時(shí)候,只需要關(guān)心狀態(tài)的切換泣特,而不用關(guān)心這個(gè)狀態(tài)對(duì)應(yīng)的真正處理浩姥。
- 更好的擴(kuò)展性
引入了狀態(tài)處理的公共接口后,使得擴(kuò)展新的狀態(tài)變得非常容易状您,只需要新增加一個(gè)實(shí)現(xiàn)狀態(tài)處理的公共接口的實(shí)現(xiàn)類,然后在進(jìn)行狀態(tài)維護(hù)的地方兜挨,設(shè)置狀態(tài)變化到這個(gè)新的狀態(tài)即可膏孟。
- 顯式化進(jìn)行狀態(tài)轉(zhuǎn)換
狀態(tài)模式為不同的狀態(tài)引入獨(dú)立的對(duì)象,使得狀態(tài)的轉(zhuǎn)換變得更加明確拌汇。而且狀態(tài)對(duì)象可以保證上下文不會(huì)發(fā)生內(nèi)部狀態(tài)不一致的情況柒桑,因?yàn)樯舷挛闹兄挥幸粋€(gè)變量來(lái)記錄狀態(tài)對(duì)象,只要為這一個(gè)變量賦值就可以了噪舀。
- 引入太多的狀態(tài)類
狀態(tài)模式也有一個(gè)很明顯的缺點(diǎn)魁淳,一個(gè)狀態(tài)對(duì)應(yīng)一個(gè)狀態(tài)處理類,會(huì)使得程序引入太多的狀態(tài)類与倡,使程序變得雜亂界逛。
3.6 思考狀態(tài)模式##
- 狀態(tài)模式的本質(zhì)
狀態(tài)模式的本質(zhì):根據(jù)狀態(tài)來(lái)分離和選擇行為。
仔細(xì)分析狀態(tài)模式的結(jié)構(gòu)纺座,如果沒(méi)有上下文息拜,那么就退化回到只有接口和實(shí)現(xiàn)了,正是通過(guò)接口净响,把狀態(tài)和狀態(tài)對(duì)應(yīng)的行為分開(kāi)少欺,才使得通過(guò)狀態(tài)模式設(shè)計(jì)的程序易于擴(kuò)展和維護(hù)。
而上下文主要負(fù)責(zé)的是公共的狀態(tài)驅(qū)動(dòng)馋贤,每當(dāng)狀態(tài)發(fā)生改變的時(shí)候赞别,通常都是回調(diào)上下文來(lái)執(zhí)行狀態(tài)對(duì)應(yīng)的功能
。當(dāng)然配乓,上下文自身也可以維護(hù)狀態(tài)的變化仿滔,另外,上下文通常還會(huì)作為多個(gè)狀態(tài)處理類之間的數(shù)據(jù)載體扰付,在多個(gè)狀態(tài)處理類之間傳遞數(shù)據(jù)堤撵。
- 何時(shí)選用狀態(tài)模式
建議在如下情況中,選用狀態(tài)模式:
如果一個(gè)對(duì)象的行為取決于它的狀態(tài)羽莺,而且它必須在運(yùn)行時(shí)刻根據(jù)狀態(tài)來(lái)改變它的行為
实昨。可以使用狀態(tài)模式盐固,來(lái)把狀態(tài)和行為分離開(kāi)荒给,雖然分離開(kāi)了丈挟,但狀態(tài)和行為是有對(duì)應(yīng)關(guān)系的,可以在運(yùn)行期間志电,通過(guò)改變狀態(tài)曙咽,就能夠調(diào)用到該狀態(tài)對(duì)應(yīng)的狀態(tài)處理對(duì)象上去,從而改變對(duì)象的行為挑辆。
如果一個(gè)操作中含有龐大的多分支語(yǔ)句例朱,而且這些分支依賴于該對(duì)象的狀態(tài)
∮悴酰可以使用狀態(tài)模式洒嗤,把各個(gè)分支的處理分散包裝到單獨(dú)的對(duì)象處理類里面,這樣魁亦,這些分支對(duì)應(yīng)的對(duì)象就可以不依賴于其它對(duì)象而獨(dú)立變化了渔隶。
3.7 相關(guān)模式##
- 狀態(tài)模式和策略模式
這是兩個(gè)結(jié)構(gòu)相同,功能各異的模式洁奈,具體的在策略模式里面講過(guò)了间唉,這里就不再贅述了。
- 狀態(tài)模式和觀察者模式
這兩個(gè)模式乍一看利术,功能是很相似的呈野,但是又有區(qū)別,可以組合使用氯哮。
這兩個(gè)模式都是在狀態(tài)發(fā)生改變的時(shí)候觸發(fā)行為际跪,只不過(guò)觀察者模式的行為是固定的,那就是通知所有的觀察者喉钢,而狀態(tài)模式是根據(jù)狀態(tài)來(lái)選擇不同的處理
姆打。
從表面來(lái)看,兩個(gè)模式功能相似肠虽,觀察者模式中的被觀察對(duì)象就好比狀態(tài)模式中的上下文幔戏,觀察者模式中當(dāng)被觀察對(duì)象的狀態(tài)發(fā)生改變的時(shí)候,觸發(fā)的通知所有觀察者的方法
税课;就好比是狀態(tài)模式中闲延,根據(jù)狀態(tài)的變化,選擇對(duì)應(yīng)的狀態(tài)處理韩玩。
但實(shí)際這兩個(gè)模式是不同的垒玲,觀察者模式的目的是在被觀察者的狀態(tài)發(fā)生改變的時(shí)候,觸發(fā)觀察者聯(lián)動(dòng)找颓,具體如何處理觀察者模式不管
合愈;而狀態(tài)模式的主要目的在于根據(jù)狀態(tài)來(lái)分離和選擇行為,當(dāng)狀態(tài)發(fā)生改變的時(shí)候,動(dòng)態(tài)改變行為
佛析。
這兩個(gè)模式是可以組合使用的益老,比如在觀察者模式的觀察者部分,當(dāng)被觀察對(duì)象的狀態(tài)發(fā)生了改變寸莫,觸發(fā)通知了所有的觀察者過(guò)后捺萌,觀察者該怎么處理呢?這個(gè)時(shí)候就可以使用狀態(tài)模式膘茎,根據(jù)通知過(guò)來(lái)的狀態(tài)選擇相應(yīng)的處理桃纯。
- 狀態(tài)模式和單例模式
這兩個(gè)模式可以組合使用,可以把狀態(tài)模式中的狀態(tài)處理類實(shí)現(xiàn)成單例披坏。
- 狀態(tài)模式和享元模式
這兩個(gè)模式可以組合使用慈参。
由于狀態(tài)模式把狀態(tài)對(duì)應(yīng)的行為分散到多個(gè)狀態(tài)對(duì)象中,會(huì)造成很多細(xì)粒度的狀態(tài)對(duì)象刮萌,可以把這些狀態(tài)處理對(duì)象通過(guò)享元模式來(lái)共享,從而節(jié)省資源娘扩。