《Head First 設計模式》:狀態模式

驚卻一目發表於2020-09-21

正文

一、定義

狀態模式允許物件在內部狀態改變時改變它的行為,物件看起來好像修改了它的類。

要點:

  • 狀態模式允許一個物件基於內部狀態而擁有不同的行為。
  • 狀態模式將狀態封裝成為獨立的類,並將動作委託到代表當前狀態的物件。
  • 通過將每個狀態封裝進一個類,我們把以後需要做的任何改變區域性化了。

二、實現步驟

1、建立狀態介面

/**
 * 狀態介面
 */
public interface State {
    
    /**
     * 根據狀態進行處理的方法
     */
    public void handle();
}

2、在持有狀態的類中,將請求委託給狀態類

/**
 * 持有狀態的上下文類
 */
public class Context {
    
    private State state;
    
    public State getState() {
        return state;
    }

    public void setState(State state) {
        this.state = state;
    }
    
    /**
     * 接收請求,並將請求委託給狀態類
     */
    public void request() {
        state.handle();
    }
}

3、建立具體的狀態,並實現狀態介面

(1)具體狀態A

/**
 * 具體狀態A
 */
public class ConcreteStateA implements State {
    
    Context context;
    
    public ConcreteStateA() {
        context = new Context();
    }

    @Override
    public void handle() {
        // 實現該狀態下相應的行為
        System.out.println("Context is in A state, and start to do something...");
        context.setState(this);
    }
}

(2)具體狀態B

/**
 * 具體狀態B
 */
public class ConcreteStateB implements State {
    
    Context context;
    
    public ConcreteStateB() {
        context = new Context();
    }

    @Override
    public void handle() {
        // 實現該狀態下相應的行為
        System.out.println("Context is in B state, and start to do something...");
        context.setState(this);
    }
}

4、通過改變狀態,來改變上下文類的行為

public class Test {

    public static void main(String[] args) {
        // 上下文
        Context context = new Context();
        // 狀態
        State stateA = new ConcreteStateA();
        State stateB = new ConcreteStateB();
        // 通過狀態改變行為
        context.setState(stateA);
        context.request();
        context.setState(stateB);
        context.request();
    }
}

三、舉個例子

1、背景

萬能糖果公司打算使用 Java 來實現糖果機的控制器。他們希望設計能夠儘量有彈性而且好維護,因為將來可能要為糖果機增加更多的行為。

糖果機的工作流程如下:

2、實現

(1)建立狀態介面,並定義相應的糖果機行為

/**
 * 狀態介面
 */
public interface State {
    
    /**
     * 投入25分錢
     */
    public void insertQuarter();
    
    /**
     * 退回25分錢
     */
    public void ejectQuarter();
    
    /**
     * 轉動曲柄
     */
    public void turnCrank();
    
    /**
     * 發放糖果
     */
    public void dispense();
}

(2)建立糖果機

將傳遞給糖果機的請求,委託給狀態類。

/**
 * 糖果機
 */
public class GumballMachine {

    State soldOutState;
    State noQuarterState;
    State hasQuarterState;
    State soldState;
    
    State state = soldOutState;
    int gumballCount = 0;
    
    public GumballMachine(int initGumballCount) {
        soldOutState = new SoldOutState(this);
        noQuarterState = new NoQuarterState(this);
        hasQuarterState = new HasQuarterState(this);
        soldState = new SoldState(this);
        // 初始化糖果數量
        this.gumballCount = initGumballCount;
        // 初始化糖果機狀態
        if (initGumballCount > 0) {
            state = noQuarterState;
        } else {
            state = soldOutState;
        }
    }
    
    /**
     * 投入25分錢
     */
    public void insertQuarter() {
        state.insertQuarter();
    }
    
    /**
     * 退回25分錢
     */
    public void ejectQuarter() {
        state.ejectQuarter();
    }
    
    /**
     * 轉動曲柄
     */
    public void turnCrank() {
        state.turnCrank();
        state.dispense();
    }
    
    /**
     * 發放糖果
     */
    public void releaseBall() {
        System.out.println("A gumball comes rolling out the slot...");
        if (gumballCount > 0) {
            gumballCount = gumballCount -1;
        }
    }
    
    public void setState(State state) {
        this.state = state;
    }
    
    public int getGumballCount() {
        return gumballCount;
    }

    public State getSoldOutState() {
        return soldOutState;
    }

    public State getNoQuarterState() {
        return noQuarterState;
    }

    public State getHasQuarterState() {
        return hasQuarterState;
    }

    public State getSoldState() {
        return soldState;
    }
}

(3)建立具體的狀態,並實現狀態介面

/**
 * 未投入25分錢狀態
 */
public class NoQuarterState implements State {

    GumballMachine gumballMachine;

    public NoQuarterState(GumballMachine gumballMachine) {
        this.gumballMachine = gumballMachine;
    }
    
    @Override
    public void insertQuarter() {
        // 投入25分錢,並轉到已投入25分錢狀態
        System.out.println("You inserted a quarter");
        gumballMachine.setState(gumballMachine.getHasQuarterState());
    }

    @Override
    public void ejectQuarter() {
        // 當前為未投入25分錢狀態,不能退回25分錢
        System.out.println("You haven't inserted a quarter");
    }

    @Override
    public void turnCrank() {
        // 當前為未投入25分錢狀態,不能轉動曲柄
        System.out.println("You truned, but there's no quarter");
    }

    @Override
    public void dispense() {
        // 當前為未投入25分錢狀態,不能發放糖果
        System.out.println("You need to pay first");
    }
}
/**
 * 已投入25分錢狀態
 */
public class HasQuarterState implements State {
    
    GumballMachine gumballMachine;

    public HasQuarterState(GumballMachine gumballMachine) {
        this.gumballMachine = gumballMachine;
    }
    
    @Override
    public void insertQuarter() {
        // 當前為已投入25分錢狀態,不能再次投入
        System.out.println("You can't insert another quarter");
    }

    @Override
    public void ejectQuarter() {
        // 退回25分錢,並將狀態轉到未投入25分錢狀態
        System.out.println("Quarter returned");
        gumballMachine.setState(gumballMachine.getNoQuarterState());
    }

    @Override
    public void turnCrank() {
        // 轉動曲柄,並將狀態轉為售出狀態
        System.out.println("You turned...");
        gumballMachine.setState(gumballMachine.getSoldState());
    }

    @Override
    public void dispense() {
        // 當前為已投入25分錢狀態,還未轉動曲柄,不能發放糖果
        System.out.println("No gumball dispensed");
    }
}
/**
 * 售出狀態
 */
public class SoldState implements State {

    GumballMachine gumballMachine;

    public SoldState(GumballMachine gumballMachine) {
        this.gumballMachine = gumballMachine;
    }
    
    @Override
    public void insertQuarter() {
        // 當前為售出狀態,不能再次投入25分錢
        System.out.println("Please wait, we're already giving you a gumball");
    }

    @Override
    public void ejectQuarter() {
        // 當前為售出狀態,不能退回25分錢
        System.out.println("Sorry, you already truned the crank");
    }

    @Override
    public void turnCrank() {
        // 當前為售出狀態,不能再次轉動曲柄
        System.out.println("Turning twice doesn't get you another gumball!");
    }

    @Override
    public void dispense() {
        // 發放糖果
        gumballMachine.releaseBall();
        if (gumballMachine.getGumballCount() > 0) {
            gumballMachine.setState(gumballMachine.getNoQuarterState());
        } else {
            System.out.println("Oops, out of gumballs!");
            gumballMachine.setState(gumballMachine.getSoldOutState());
        }
    }
}
/**
 * 售罄狀態
 */
public class SoldOutState implements State {

    GumballMachine gumballMachine;

    public SoldOutState(GumballMachine gumballMachine) {
        this.gumballMachine = gumballMachine;
    }
    
    @Override
    public void insertQuarter() {
        // 當前為售罄狀態,不能投入25分錢
        System.out.println("You can't insert a quarter, the machine is sold out");
    }

    @Override
    public void ejectQuarter() {
        // 當前為售罄狀態,不能要求退回25分錢
        System.out.println("You can't eject, you haven't inserted a quarter yet");
    }

    @Override
    public void turnCrank() {
        // 當前為售罄狀態,不能轉動曲柄
        System.out.println("You turned, but there are no gumballs");
    }

    @Override
    public void dispense() {
        // 當前為售罄狀態,不能發放糖果
        System.out.println("No gumball dispensed");
    }
}

(4)操作糖果機

public class Test {
    
    public static void main(String[] args) {
        // 糖果機
        GumballMachine gumballMachine = new GumballMachine(5);
        // 正常操作
        gumballMachine.insertQuarter();
        gumballMachine.turnCrank();
        System.out.println("-----------------------");
        // 異常操作
        gumballMachine.insertQuarter();
        gumballMachine.ejectQuarter();
        gumballMachine.turnCrank();
    }
}

相關文章