設(shè)計(jì)模式之狀態(tài)模式

原文:https://blog.csdn.net/liaodehong/article/details/52079502

模式簡介

允許一個(gè)對(duì)象在其內(nèi)部狀態(tài)改變時(shí)改變它的行為。對(duì)象看起來似乎修改了它的類胎挎,(State Pattern)是設(shè)計(jì)模式的一種若债,屬于行為模式壤玫。

定義

(源于Design Pattern):當(dāng)一個(gè)對(duì)象的內(nèi)在狀態(tài)改變時(shí)允許改變其行為杆融,這個(gè)對(duì)象看起來像是改變了其類体箕。
狀態(tài)模式主要解決的是當(dāng)控制一個(gè)對(duì)象狀態(tài)的條件表達(dá)式過于復(fù)雜時(shí)的情況痕钢。把狀態(tài)的判斷邏輯轉(zhuǎn)移到表示不同狀態(tài)的一系列類中香到,可以把復(fù)雜的判斷邏輯簡化鱼冀。
模式中的角色

1 上下文環(huán)境(Context):它定義了客戶程序需要的接口并維護(hù)一個(gè)具體狀態(tài)角色的實(shí)例,將與狀態(tài)相關(guān)的操作委托給當(dāng)前的Concrete State對(duì)象來處理悠就。

2 抽象狀態(tài)(State):定義一個(gè)接口以封裝使用上下文環(huán)境的的一個(gè)特定狀態(tài)相關(guān)的行為千绪。

3 具體狀態(tài)(Concrete State):實(shí)現(xiàn)抽象狀態(tài)定義的接口。

狀態(tài)模式的類圖


image.png

這里來看看狀態(tài)模式的標(biāo)準(zhǔn)代碼梗脾;

首先我們先定義一個(gè)State抽象狀態(tài)類荸型,里面定義了一個(gè)接口以封裝 與Context的一個(gè)特定狀態(tài)相關(guān)的行為;

/**
 * 抽象狀態(tài)類
 * @author gh
 *
 */
public abstract class State {
    
    public abstract void Handle(Context context);
}

接著再去聲明一個(gè)ConcreteState具體狀態(tài)類炸茧,每一個(gè)子類實(shí)現(xiàn)一個(gè)與Context的一個(gè)狀態(tài)的相關(guān)的行為瑞妇。

public class ConcreteStateA extends State{
 
    @Override
    public void Handle(Context context) {
        context.setState(new ConcreteStateB()); //設(shè)置A的下一個(gè)狀態(tài)是B
        
    }
 
}
class ConcreteStateB extends State{
 
    @Override
    public void Handle(Context context) {
        context.setState(new ConcreteStateA()); //設(shè)置B的下一個(gè)狀態(tài)是A
    }
    
}

Context類,維護(hù)一個(gè)ConcreteState子類的實(shí)例梭冠,這個(gè)實(shí)例定義當(dāng)前的狀態(tài)

/**
 * 定義當(dāng)前的狀態(tài)
 * @author gh
 *
 */
public class Context {
    State state;
 
    public Context(State state) { //定義Context的初始狀態(tài)
        super();
        this.state = state;
    }
 
    public State getState() {
        return state;
    }
 
    public void setState(State state) {
        this.state = state;
        System.out.println("當(dāng)前狀態(tài)為"+state);
    }
    public void request(){
        state.Handle(this); //對(duì)請(qǐng)求做處理并且指向下一個(gè)狀態(tài)
    }
}

提到狀態(tài)模式辕狰,讓我想到了工作流,工作流就是控制一個(gè)一個(gè)的節(jié)點(diǎn)狀態(tài)來實(shí)現(xiàn)節(jié)點(diǎn)的跳轉(zhuǎn)控漠,最后來控制流程蔓倍。


image.png

如果上面發(fā)起了一個(gè)請(qǐng)假流程,這個(gè)時(shí)候第一個(gè)節(jié)點(diǎn)就是部門領(lǐng)導(dǎo)審核盐捷,部門領(lǐng)導(dǎo)審核通過會(huì)繼續(xù)往下走偶翅,如果不通過那么有兩種狀態(tài),一種是直接駁回請(qǐng)求碉渡,領(lǐng)導(dǎo)說聚谁,項(xiàng)目最近很急,任何人都不能請(qǐng)假爆价,還有一種是你寫的請(qǐng)假申請(qǐng)單不對(duì)垦巴,要退回整改重新寫。審核通過后就進(jìn)入下一個(gè)節(jié)點(diǎn)铭段,人力資源部門審核骤宣,當(dāng)然人力資源也可以駁回請(qǐng)求,或者要你重新整改序愚,人力資源審核通過之后就可以休假了憔披,這個(gè)時(shí)候還可以選擇是否發(fā)送Email。

/**
 * 節(jié)點(diǎn)接口
 * @author gh
 *
 */
public abstract class Node {
    private static String name; //當(dāng)前節(jié)點(diǎn)名稱
    //節(jié)點(diǎn)跳轉(zhuǎn)
    public abstract void nodeHandle(FlowContext context);
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    
    
}

相當(dāng)于State類爸吮,這里維護(hù)一個(gè)節(jié)點(diǎn)名稱芬膝。

/**
 * 領(lǐng)導(dǎo)節(jié)點(diǎn)
 * 
 * @author gh
 * 
 */
public class LeadNode extends Node {
    @Override
    public void nodeHandle(FlowContext context) {
        //根據(jù)當(dāng)前流程的狀態(tài),來控制流程的走向
        //先判斷流程是否結(jié)束
        if(!context.isFlag()){
        System.out.println(context.getMessage()); //先讀取申請(qǐng)的內(nèi)容
        if(context!=null&&3==context.getStatus()){ //只有出于已經(jīng)申請(qǐng)的狀態(tài)才又部門領(lǐng)導(dǎo)審核
            //設(shè)置當(dāng)前節(jié)點(diǎn)的名稱
            setName("張經(jīng)理");
            //加上審核意見
            context.setMessage(context.getMessage()+getName()+"審核通過;");
            //審核通過
            context.setStatus(0); //審核通過并指向下一個(gè)節(jié)點(diǎn)
            context.setNode(new HrNode());
            context.getNode().nodeHandle(context);
        }
    }else{
        System.err.println("流程已經(jīng)結(jié)束");
        }
    }
}

這里創(chuàng)建了一個(gè)領(lǐng)導(dǎo)節(jié)點(diǎn)形娇,用來維護(hù)領(lǐng)導(dǎo)審核的流程锰霜,審核通過會(huì)交給HR審核;

public class HrNode extends Node {
 
    @Override
    public void nodeHandle(FlowContext context) {
        //先判斷流程是否結(jié)束
        if(!context.isFlag()){
        // 根據(jù)當(dāng)前流程的狀態(tài)桐早,來控制流程的走向
        if (context != null &&
                0 == context.getStatus()) { //只有上一級(jí)審核通過后才能輪到HR審核
            // 設(shè)置當(dāng)前節(jié)點(diǎn)的名稱
            setName("HR李");
            //讀取上一級(jí)的審核內(nèi)容并加上自己的意見
            System.out.println(context.getMessage()+getName()+"審核通過");
            // 審核通過
            context.setStatus(0); //HR審核通過并指向下一個(gè)節(jié)點(diǎn) ,如果沒有下一個(gè)節(jié)點(diǎn)就把狀態(tài)設(shè)置為終結(jié)
            context.setFlag(true);
            
        }
        }else{
            System.out.println("流程已經(jīng)結(jié)束");
        }
    }
 
}

這里HR審核通過并把節(jié)點(diǎn)設(shè)置為完結(jié)狀態(tài)癣缅;

/**
 * 流程控制
 * 
 * @author gh
 * 
 */
public class FlowContext {
    private boolean flag; // 代表流程是否結(jié)束
    /**
     * 流程狀態(tài) 0:通過 1:駁回 2.退回整改 3.已申請(qǐng)
     * 
     */
    private int status;
 
    private String message; // 消息
    private Node node; // 節(jié)點(diǎn)信息
    public boolean isFlag() {
        return flag;
    }
 
    public void setFlag(boolean flag) {
        this.flag = flag;
    }
 
    public int getStatus() {
        return status;
    }
 
    public void setStatus(int status) {
        this.status = status;
    }
 
    public String getMessage() {
        return message;
    }
 
    public void setMessage(String message) {
        this.message = message;
    }
 
    public Node getNode() {
        return node;
    }
 
    public void setNode(Node node) {
        this.node = node;
    }
 
    public static boolean start(FlowContext context) {
        Node node = new LeadNode();
        context.setNode(node); // 設(shè)置初始節(jié)點(diǎn)
        context.setStatus(3); // 設(shè)置狀態(tài)為申請(qǐng)中
        context.getNode().nodeHandle(context); // 發(fā)起請(qǐng)求
        // 最后要知道是否申請(qǐng)成功
        //判斷當(dāng)前是最后一個(gè)節(jié)點(diǎn)并且審核通過,而且流程結(jié)束
        if("HR李".equals(node.getName())&&0==context.getStatus()&&context.isFlag()){
            System.out.println("審核通過,流程結(jié)束");
            return true;
        }else{
            System.out.println("審核未通過哄酝,流程已經(jīng)結(jié)束");
            return false;
        }
    }
 
    public FlowContext() {
        super();
    }
    
}

這里維護(hù)一個(gè)流程控制類友存,它會(huì)在HR和LEAD節(jié)點(diǎn)之后傳遞,并分別由他們?nèi)ゾS護(hù)各自的節(jié)點(diǎn)陶衅。
最后寫一個(gè)測試類測試一下:

public static void main(String[] args) {
    FlowContext context=new FlowContext();
       context.setMessage("本人王小二屡立,因?yàn)槭患依镉惺虑椋砸嗾?qǐng)三天假搀军,希望公司能夠?qū)徍送ㄟ^");
    context.start(context); 
 }

打印結(jié)果如下
本人王小二膨俐,因?yàn)槭患依镉惺虑椋砸嗾?qǐng)三天假罩句,希望公司能夠?qū)徍送ㄟ^
本人王小二吟策,因?yàn)槭患依镉惺虑椋砸嗾?qǐng)三天假的止,希望公司能夠?qū)徍送ㄟ^張經(jīng)理審核通過;HR李審核通過
審核通過,流程結(jié)束檩坚;

上面這個(gè)例子只是很簡單的模仿了一下工作流控制狀態(tài)的跳轉(zhuǎn)。狀態(tài)模式最主要的好處就是把狀態(tài)的判斷與控制放到了其服務(wù)端的內(nèi)部诅福,使得客戶端不需要去寫很多代碼判斷匾委,來控制自己的節(jié)點(diǎn)跳轉(zhuǎn),而且這樣實(shí)現(xiàn)的話氓润,我們可以把每個(gè)節(jié)點(diǎn)都分開來處理赂乐,當(dāng)流程流轉(zhuǎn)到某個(gè)節(jié)點(diǎn)的時(shí)候,可以去寫自己的節(jié)點(diǎn)流轉(zhuǎn)方法咖气。當(dāng)然狀態(tài)模式的缺點(diǎn)也很多挨措,比如類的耦合度比較高挖滤,基本上三個(gè)類要同時(shí)去寫,而且會(huì)創(chuàng)建很多的節(jié)點(diǎn)類浅役。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末斩松,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子觉既,更是在濱河造成了極大的恐慌惧盹,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,376評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件瞪讼,死亡現(xiàn)場離奇詭異钧椰,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)符欠,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,126評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門嫡霞,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人希柿,你說我怎么就攤上這事秒际。” “怎么了狡汉?”我有些...
    開封第一講書人閱讀 156,966評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵娄徊,是天一觀的道長。 經(jīng)常有香客問我盾戴,道長寄锐,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,432評(píng)論 1 283
  • 正文 為了忘掉前任尖啡,我火速辦了婚禮橄仆,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘衅斩。我一直安慰自己盆顾,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,519評(píng)論 6 385
  • 文/花漫 我一把揭開白布畏梆。 她就那樣靜靜地躺著您宪,像睡著了一般。 火紅的嫁衣襯著肌膚如雪奠涌。 梳的紋絲不亂的頭發(fā)上宪巨,一...
    開封第一講書人閱讀 49,792評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音溜畅,去河邊找鬼捏卓。 笑死,一個(gè)胖子當(dāng)著我的面吹牛慈格,可吹牛的內(nèi)容都是我干的怠晴。 我是一名探鬼主播遥金,決...
    沈念sama閱讀 38,933評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼蒜田!你這毒婦竟也來了稿械?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,701評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤物邑,失蹤者是張志新(化名)和其女友劉穎溜哮,沒想到半個(gè)月后滔金,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體色解,經(jīng)...
    沈念sama閱讀 44,143評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,488評(píng)論 2 327
  • 正文 我和宋清朗相戀三年餐茵,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了科阎。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,626評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡忿族,死狀恐怖锣笨,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情道批,我是刑警寧澤错英,帶...
    沈念sama閱讀 34,292評(píng)論 4 329
  • 正文 年R本政府宣布,位于F島的核電站隆豹,受9級(jí)特大地震影響椭岩,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜璃赡,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,896評(píng)論 3 313
  • 文/蒙蒙 一判哥、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧碉考,春花似錦塌计、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,742評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至墙贱,卻和暖如春技扼,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背嫩痰。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評(píng)論 1 265
  • 我被黑心中介騙來泰國打工剿吻, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人串纺。 一個(gè)月前我還...
    沈念sama閱讀 46,324評(píng)論 2 360
  • 正文 我出身青樓丽旅,卻偏偏與公主長得像椰棘,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子榄笙,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,494評(píng)論 2 348

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

  • 1.狀態(tài)模式的定義 定義對(duì)象間的一種一對(duì)多的依賴關(guān)系,當(dāng)一個(gè)對(duì)象的狀態(tài)發(fā)生改變時(shí),所有依賴于它的對(duì)象都得到通知并被...
    書上的煙灰閱讀 942評(píng)論 0 4
  • 狀態(tài)模式 面向?qū)ο缶幊讨τ谠趯?duì)象交互時(shí)改變它們的狀態(tài)邪狞。在很多問題中,有限狀態(tài)機(jī)(通常名為狀態(tài)機(jī))是一個(gè)非常方便的...
    英武閱讀 5,513評(píng)論 0 51
  • 概述 狀態(tài)模式中的行為是由狀態(tài)決定的茅撞,不同的狀態(tài)有不同的行為帆卓,狀態(tài)模式把對(duì)象的行為包裝在不同的具體狀態(tài)對(duì)象里,每一...
    ikonan閱讀 460評(píng)論 0 0
  • 狀態(tài)模式代碼原型 解決的問題:主要解決的是當(dāng)控制一個(gè)對(duì)象狀態(tài)轉(zhuǎn)換的條件表達(dá)式過于復(fù)雜時(shí)的情況米丘。把狀態(tài)的判斷邏輯轉(zhuǎn)移...
    _涼笙閱讀 631評(píng)論 0 2
  • 互聯(lián)網(wǎng)發(fā)展到今天剑令,一個(gè)大型項(xiàng)目的完成,往往需要一個(gè)團(tuán)隊(duì)的通力合作拄查,而git就是一種幫助程序員們共同工作的工具吁津,下面...
    show_蕭佩閱讀 166評(píng)論 0 0