【行為型模式二十】狀態(tài)模式-2(State)

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é)束。流程圖如圖所示:

簡(jiǎn)單的請(qǐng)假流程示意圖

在實(shí)際開(kāi)發(fā)中森爽,如果不考慮使用工作流軟件恨豁,按照流程來(lái)自己實(shí)現(xiàn)的話,這個(gè)流程基本的運(yùn)行過(guò)程簡(jiǎn)化描述如下:

  1. UI操作:請(qǐng)假人填寫請(qǐng)假單爬迟,提出請(qǐng)假申請(qǐng)
  2. 后臺(tái)處理:保存請(qǐng)假單數(shù)據(jù)到數(shù)據(jù)庫(kù)中橘蜜,然后為項(xiàng)目經(jīng)理創(chuàng)建一個(gè)工作,把工作信息保存到數(shù)據(jù)庫(kù)中
  3. UI操作:項(xiàng)目經(jīng)理登錄系統(tǒng),獲取自己的工作列表
  4. 后臺(tái)處理:從數(shù)據(jù)庫(kù)中獲取相應(yīng)的工作列表
  5. UI操作:項(xiàng)目經(jīng)理完成審核工作计福,提交保存
  6. 后臺(tái)處理:處理項(xiàng)目經(jīng)理審核的業(yè)務(wù)跌捆,保存審核的信息到數(shù)據(jù)庫(kù)。同時(shí)判斷后續(xù)的工作象颖,如果是需要人員參與的佩厚,就為參與下一個(gè)工作的人員創(chuàng)建工作,把工作信息保存到數(shù)據(jù)庫(kù)中
  7. UI操作:部門經(jīng)理登錄系統(tǒng)说订,獲取自己的工作列表抄瓦,基本上是重復(fù)第3步
  8. 后臺(tái)處理:從數(shù)據(jù)庫(kù)中獲取相應(yīng)的工作列表,基本上是重復(fù)第4步
  9. UI操作:部門經(jīng)理完成審核工作陶冷,提交保存钙姊,基本上是重復(fù)第5步
  10. 后臺(tái)處理:類推,基本上是重復(fù)第6步
  1. 實(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)的功能眶拉。

  1. 使用狀態(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)示意圖猜拾,如圖所示:

用狀態(tài)模式實(shí)現(xiàn)的流程的程序結(jié)構(gòu)示意圖
  1. 改進(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é)果:不同意
  1. 小結(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)##

  1. 簡(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ǔ)句

  1. 更好的分離狀態(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)的真正處理浩姥。

  1. 更好的擴(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)即可膏孟。

  1. 顯式化進(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è)變量賦值就可以了噪舀。

  1. 引入太多的狀態(tài)類

狀態(tài)模式也有一個(gè)很明顯的缺點(diǎn)魁淳,一個(gè)狀態(tài)對(duì)應(yīng)一個(gè)狀態(tài)處理類,會(huì)使得程序引入太多的狀態(tài)類与倡,使程序變得雜亂界逛。

3.6 思考狀態(tài)模式##

  1. 狀態(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ù)堤撵。

  1. 何時(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)模式##

  1. 狀態(tài)模式和策略模式

這是兩個(gè)結(jié)構(gòu)相同,功能各異的模式洁奈,具體的在策略模式里面講過(guò)了间唉,這里就不再贅述了。

  1. 狀態(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)的處理桃纯。

  1. 狀態(tài)模式和單例模式

這兩個(gè)模式可以組合使用,可以把狀態(tài)模式中的狀態(tài)處理類實(shí)現(xiàn)成單例披坏。

  1. 狀態(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é)省資源娘扩。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末着茸,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子琐旁,更是在濱河造成了極大的恐慌涮阔,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,113評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件灰殴,死亡現(xiàn)場(chǎng)離奇詭異敬特,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)牺陶,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門伟阔,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人掰伸,你說(shuō)我怎么就攤上這事皱炉。” “怎么了狮鸭?”我有些...
    開(kāi)封第一講書(shū)人閱讀 153,340評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵合搅,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我歧蕉,道長(zhǎng)灾部,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,449評(píng)論 1 279
  • 正文 為了忘掉前任惯退,我火速辦了婚禮赌髓,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己春弥,他們只是感情好呛哟,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著匿沛,像睡著了一般扫责。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上逃呼,一...
    開(kāi)封第一講書(shū)人閱讀 49,166評(píng)論 1 284
  • 那天鳖孤,我揣著相機(jī)與錄音,去河邊找鬼抡笼。 笑死苏揣,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的推姻。 我是一名探鬼主播平匈,決...
    沈念sama閱讀 38,442評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼藏古!你這毒婦竟也來(lái)了增炭?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,105評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤拧晕,失蹤者是張志新(化名)和其女友劉穎隙姿,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體厂捞,經(jīng)...
    沈念sama閱讀 43,601評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡输玷,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了靡馁。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片欲鹏。...
    茶點(diǎn)故事閱讀 38,161評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖奈嘿,靈堂內(nèi)的尸體忽然破棺而出貌虾,到底是詐尸還是另有隱情,我是刑警寧澤裙犹,帶...
    沈念sama閱讀 33,792評(píng)論 4 323
  • 正文 年R本政府宣布尽狠,位于F島的核電站,受9級(jí)特大地震影響叶圃,放射性物質(zhì)發(fā)生泄漏袄膏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評(píng)論 3 307
  • 文/蒙蒙 一掺冠、第九天 我趴在偏房一處隱蔽的房頂上張望沉馆。 院中可真熱鬧码党,春花似錦、人聲如沸斥黑。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,352評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)锌奴。三九已至兽狭,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間鹿蜀,已是汗流浹背箕慧。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,584評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留茴恰,地道東北人颠焦。 一個(gè)月前我還...
    沈念sama閱讀 45,618評(píng)論 2 355
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像往枣,于是被迫代替她去往敵國(guó)和親伐庭。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評(píng)論 2 344

推薦閱讀更多精彩內(nèi)容

  • 1 場(chǎng)景問(wèn)題# 1.1 實(shí)現(xiàn)在線投票## 考慮一個(gè)在線投票的應(yīng)用分冈,要實(shí)現(xiàn)控制同一個(gè)用戶只能投一票似忧,如果一個(gè)用戶反復(fù)...
    七寸知架構(gòu)閱讀 1,933評(píng)論 7 53
  • 1.初識(shí)狀態(tài)模式 定義 運(yùn)行一個(gè)對(duì)象在其內(nèi)部狀態(tài)改變時(shí)改變它的行為。對(duì)象看起來(lái)似乎修改了它的類丈秩。 結(jié)構(gòu)和說(shuō)明Con...
    王偵閱讀 412評(píng)論 0 3
  • 作者?/北南 推薦?/秘冬 第1章此人不太順眼 丁漢白留學(xué)回來(lái)時(shí)也是盛夏,晃眼間已經(jīng)一年了淳衙。 盛夏的街上站不住人蘑秽,...
    秘冬閱讀 2,065評(píng)論 0 0
  • 這次讀書(shū)會(huì)遇到了清明節(jié),怎樣和孩子解讀清明節(jié)呢箫攀?也沒(méi)有想到什么肠牲,只是覺(jué)得要讓孩子了解這一節(jié)氣。早晨醒來(lái)靴跛,...
    漫_63f1閱讀 166評(píng)論 0 0
  • label.lineBreakMode= NSLineBreakByCharWrapping;
    有偶像包袱的程序狗閱讀 870評(píng)論 0 0