1. 程式人生 > >設計模式(十一)—— 狀態模式

設計模式(十一)—— 狀態模式

一、含義

允許物件在內部狀態改變時改變它的行為,物件看起來好像修改了它的類。也就是說當一個物件有許多狀態的時候,我們可以把每個物件抽離出來作為一個具體的類。

二、要點

1.狀態模式允許一個物件基於內部狀態而擁有不同的行為。

2.通過將每個狀態封裝進一個類,我們把以後需要做的任何改變區域性化了。

3.使用狀態模式通常會導致設計中類的數目大量增加。

三、實戰分析外觀模式

首先看一下狀態模式的類圖:

例如現在有需要自動售貨機,現在假設一個自動售貨機只賣一種飲料,只需5塊錢,對於售貨機有四種狀態就是,沒有投錢,投入5塊錢,出售飲料,飲料售空。現在讓我們用程式碼,來編寫自動售貨機這個收穫流程:

public class DrinksMachine {
    
    //對應自動售貨機的四種狀態
    final static int SOLD_OUT = 0;
    
    final static int NO_QUARTER = 1;
    
    final static int HAS_QUARTER = 2;
    
    final static int SOLD = 3;
    
    //記錄當前狀態
    int state = SOLD_OUT;
    
    //記錄飲料數目
    int count = 0;

    public DrinksMachine(int count) {
        this.count = count;
        if (count > 0) {
            state = NO_QUARTER;
        }
    }
    
    //當我們投入5塊錢時,會執行這個方法
    public void insertQuarter() {
        if (state == HAS_QUARTER) {
            System.out.println("已投錢,請勿重複投幣");
        } else if (state == NO_QUARTER) {
            state = HAS_QUARTER;
            System.out.println("投幣成功");

        } else if (state == SOLD_OUT) {
            System.out.println("請莫投幣,飲料已售空");
        } else if (state == SOLD) {
            System.out.println("請等待,我們正在出貨");
        }
    }

    //當我們嘗試退款時,執行的方法
    public void ejectQuarter() {
        if (state == HAS_QUARTER) {
            System.out.println("正在退錢,請稍等");
            state = NO_QUARTER;
        } else if (state == NO_QUARTER) {
            System.out.println("未投錢,無法退款");
        } else if (state == SOLD) {
            System.out.println("無法退錢,正在給你出貨");
        } else if (state == SOLD_OUT) {
            System.out.println("飲料已售空,未首款");
        }
    }
    
    //當我們按下按鈕準備買飲料時,會呼叫此方法
    public void turnCrank() {
        if (state == SOLD) {
            System.out.println("正在出貨請稍等,不要重複按鈕");
        } else if (state == NO_QUARTER) {
            System.out.println("還未投錢,請先投錢");
        } else if (state == SOLD_OUT) {
            System.out.println("不好意思,已售空");
        } else if (state == HAS_QUARTER) {
            System.out.println("出貨");
            state = SOLD;
            sale();
        }
    }
    
    //出售飲料方法
    public void sale() {

        if (state == SOLD) {
            System.out.println("正在出貨");
            count --;
            if (count == 0) {
                System.out.println("已售空");
                state = SOLD_OUT;
            } else {
                state = NO_QUARTER;
            }
        } else if (state == NO_QUARTER) {
            System.out.println("請先投錢");
        } else if (state == SOLD_OUT) {
            System.out.println("飲料已售空");
        } else if (state == HAS_QUARTER) {
            System.out.println("機器故障");
        }
        
    }
}

從上述程式碼可以看出,我們每個方法都要對售貨機的每個狀態都要進行判斷,這顯得程式碼複用性不高,而且如果我們可以加入新的狀態時,我們需要修改大量的程式碼,這違反了類應該對擴充套件開放,對修改關閉的原則。這個我們的狀態模式就可以大顯神威了,利用狀態模式,重寫這個自動收貨機,結合上面的類圖,我們需要把每個狀態單獨封裝成一個類,並實現一個共同的介面:

//狀態的通用介面
public interface state {
    
    void insertQuarter();
    
    void ejectQuarter();
    
    void turnCrank();
    
    void sale();
}

在編寫對應的具體的狀態類

//其中的一個例子,其他的三個狀態大體相同
public class NoQuarterState implements State {
    DrinksMachine drinksMachine;

    public NoQuarterState(DrinksMachine drinksMachine) {
        this.drinksMachine = drinksMachine;
    }

    //當有人投錢時
    @Override
    public void insertQuarter() {
        System.out.println("你已投錢");
        drinksMachine.setState(drinksMachine.getHasQuarterState());
    }

    @Override
    public void ejectQuarter() {
        System.out.println("你未投錢");
    }

    @Override
    public void turnCrank() {
        System.out.println("沒有錢,無法出售");
    }

    @Override
    public void sale() {
        System.out.println("沒有錢,無法出售");
    }
}

最後我們在將自己售貨機的程式碼給修改:

public class DrinksMachine {

    //對應著自己狀態
    State soldOutState;
    State noQuarterState;
    State hasQuarterState;
    State soldState;

    State state = soldOutState;
    int count = 0;

    public DrinksMachine(int number) {
        soldOutState = new SoldOutState(this);
        noQuarterState = new NoQuarterState(this);
        hasQuarterState = new HasQuarterState(this);
        soldState = new SoldState(this);
        this.count = number;
        if (number > 0) {
            state = noQuarterState;
        }
    }

    public void insertQuarter() {
        state.insertQuarter();
    }

    public void ejectQuarter() {
        state.ejectQuarter();
    }

    public void turnCrank() {
        state.turnCrank();
    }

    public void sale() {
        state.sale();
    }

    //每個狀態的get/set方法
}

這看著上就簡潔的多,並且在以後有新的狀態時,只需新增新的狀態類就可以。