Java教程:在Java中應(yīng)用State設(shè)計(jì)模式

字號(hào):

對(duì)象的狀態(tài)由各個(gè)屬性的當(dāng)前值構(gòu)成。當(dāng)我們調(diào)用某個(gè)對(duì)象的setXXX()方法時(shí),通常表示修改它的XXX屬性。另外,對(duì)象在執(zhí)行方法時(shí),也可能修改自己的狀態(tài)。在某些情形下,例如建立事務(wù)或機(jī)器模型時(shí),對(duì)象的狀態(tài)可能是決定其行為的關(guān)鍵因素,依賴于狀態(tài)的代碼邏輯可能遍布于類的大量方法。State模式的目標(biāo)就是簡化這類代碼,把依賴于狀態(tài)的邏輯集中到一組類,每一個(gè)類代表一種不同的狀態(tài),避免if語句嵌套過深或過于復(fù)雜,轉(zhuǎn)而依賴于多態(tài)性來調(diào)用不同的方法。
    狀態(tài)模型
    如果對(duì)象的狀態(tài)信息很關(guān)鍵,對(duì)象會(huì)擁有一些變量來指示如何根據(jù)狀態(tài)做出相應(yīng)的動(dòng)作。這些變量大量地散布于復(fù)雜的多層嵌套if語句中,來描述對(duì)象如何響應(yīng)可能出現(xiàn)的事件。用這種方式建立對(duì)象模型的缺點(diǎn)在于if語句可能變得相當(dāng)復(fù)雜一旦要修改對(duì)象的狀態(tài)模型,往往有多個(gè)方法的許多if語句需要調(diào)整。
    以傳送帶的門為例,考慮其狀態(tài)變化過程為:傳送帶的門由單個(gè)按鈕控制,并且假設(shè)初始時(shí)處于關(guān)閉狀態(tài)。按一下按鈕門開始打開,如果在門完全打開之前再次按下按鈕,門開始關(guān)閉。一旦門完全打開,它將在2秒延時(shí)之后自動(dòng)開始關(guān)閉過程。要禁止門自動(dòng)關(guān)閉,可以在門打開之后按一下按鈕。圖1描述了傳送門的狀態(tài)變化情況。它是一個(gè)UML狀態(tài)機(jī)(State Machine),其中click表示按下按鈕的動(dòng)作。顯然,與純文字描述相比UML狀態(tài)機(jī)圖示更加直觀易懂。
    按照常規(guī)的設(shè)計(jì)思路(不使用State設(shè)計(jì)模式),在模擬傳送帶工作過程的軟件中,可以使用一個(gè)Door1對(duì)象代表傳送門(如圖2所示),狀態(tài)改變事件由傳送帶軟件發(fā)送給Door1對(duì)象。
    圖1 UML狀態(tài)機(jī)
    
    圖2 狀態(tài)改變事件發(fā)送給Door1對(duì)象
    
    Door1類從Observable派生,這樣客戶程序(例如一個(gè)GUI程序)就能夠方便地了解傳送門狀態(tài)。Door1類首先定義傳送門可能處于的狀態(tài),代碼如下:
    public class Door1 extends Observable {
    public static final int CLOSED  = 1;
    public static final int OPENING = 2;
    public static final int OPEN   = 3;
    public static final int CLOSING = 4;
    public static final int STAYOPEN = 5;
    private int state = CLOSED;
    //...
    }
    status()方法返回傳送門狀態(tài)的文字描述,如下所示:
    public String status() {
    switch (state) {
    case OPENING :
    return "正在打開";
    case OPEN :
    //...
    default :
    return "已關(guān)閉";
    }
    }
    當(dāng)用戶點(diǎn)擊傳送帶的按鈕時(shí),傳送帶程序調(diào)用Door1對(duì)象的click()方法。click()方法模擬圖1所示的狀態(tài)裝換過程:
    public void click() {
    if (state == CLOSED) {
    setState(OPENING);
    }
    else if (state == OPENING || state == STAYOPEN) {
    setState(CLOSING);
    }
    else if (state == OPEN) {
    setState(STAYOPEN);
    }
    else if (state == CLOSING) {
    setState(OPENING);
    }
    }
    Door1類的setState()方法向觀察者通知傳送門狀態(tài)改變事件,代碼如下:
    private void setState(int state) {
    this.state = state;
    setChanged();
    notifyObservers();
    }