天天看點

[設計模式] 行為型:狀态模式(State Pattern)

文章目錄

    • 什麼是狀态模式
    • 設計與實作
      • 初級方案
      • 狀态模式

什麼是狀态模式

狀态模式是針對狀态機模型的程式設計套路,首先要知道狀态機是什麼。

狀态機是一種數學模型,用于對事物狀态的變化進行描述。不說那麼學術的概念了,用具體的例子來解釋會更容易了解。

電梯應該都知道吧,電梯工作過程可以簡化為幾種狀态:開門狀态、關門狀态、運作狀态、停止狀态。

  1. 開門狀态下可以關門
  2. 關門狀态下可以運作或者再次開門
  3. 運作狀态下可以停止
  4. 停止狀态下可以開門或者再次運作

用狀态圖表示就是:

[設計模式] 行為型:狀态模式(State Pattern)

在現實中,電梯停止後會自動開門,我這裡的狀态模型把“自動化”因素給剔除了,是以停止後的電梯還有可能進入運作狀态。

程式設計時如果遇到這樣的狀态機模型,就可以考慮使用狀态模式對其進行編碼。

設計與實作

初級方案

這裡先給出一個初級方案,也是通常情況下,大多數人最容易想到的方案。如果不感興趣,請直接去下一節看狀态模式的實作。

初級方案實作思路很簡單,就是熟練使用各種條件判斷。

代碼大概就像下面的樣子:

// 四種狀态枚舉
public enum StateEnum {
    OPENING, CLOSING, RUNNING, STOPPING
}

// 電梯接口定義
public interface Elevator {
    void setState(StateEnum state); // 設定目前狀态
    void open(); // 開門指令
    void close(); // 關門指令
    void run(); // 運作指令
    void stop(); // 停止指令
}

// 電梯接口實作
public class ElevatorImpl implements Elevator {

    private StateEnum state;
    
    @Override
    public void setState(StateEnum state) {
        this.state = state;
    }
    
    @Override
    public void open() {
        if (this.state == StateEnum.CLOSING || this.state == StateEnum.STOPPING) {
            System.out.println("電梯開門");
            this.setState(StateEnum.OPENING);
        }
    }
    
    @Override
    public void close() {
        if (this.state == StateEnum.OPENING) {
            System.out.println("電梯關門");
            this.setState(StateEnum.CLOSING);
        }
    }
    
    @Override
    public void run() {
        if (this.state == StateEnum.CLOSING || this.state == StateEnum.STOPPING) {
            System.out.println("電梯運作");
            this.setState(StateEnum.RUNNING);
        }
    }
    
    @Override
    public void stop() {
        if (this.state == StateEnum.RUNNING) {
            System.out.println("電梯停止");
            this.setState(StateEnum.STOPPING);
        }
    }
}

// 測試方法
public static void main(String[] args) {
    Elevator elevator = new ElevatorImpl();
    elevator.setState(StateEnum.STOPPING);
    elevator.open();
    elevator.close();
    elevator.run();
    elevator.stop();
}
           

這樣的代碼容易想到,實作起來也很容易,但是有兩個明顯的缺點:

  1. 判斷條件太多,令人眼花缭亂,寫的時候容易出錯
  2. 如果有新增狀态,每個指令方法都得修改,違反開閉原則

狀态模式

初級方案是将電梯作為一個主體,狀态為其屬性,這樣封裝代碼符合人類的自然思維,必然也會導緻内部屬性狀态判斷較為繁雜。

狀态模式則是在初級方案的基礎上,将狀态從電梯屬性中拆分出來,作為獨立的主體。電梯隻提供工作指令的接口,指令實際執行過程則是委托給各個狀态主體。

來體會一下具體的代碼設計方案:

// 電梯狀态定義
public abstract class ElevatorState {
    protected Elevator elevator;
    
    public void setElevator(Elevator elevator) {
        this.elevator = elevator;
    }
    
    public abstract void open();
    public abstract void close();
    public abstract void run();
    public abstract void stop();
}

// 電梯接口
public interface Elevator {
    ElevatorState getState(); // 擷取電梯狀态
    void setState(ElevatorState state); // 設定電梯狀态
    void open(); // 開門指令
    void close(); // 關門指令
    void run(); // 運作指令
    void stop(); // 停止指令
}

// 開門狀态
public class OpeningState extends ElevatorState {
    @Override
    public void open() {
        System.out.println("電梯開門");
    }
    
    @Override
    public void close() {
        super.elevator.setState(ElevatorImpl.CLOSING_STATE);
        super.elevator.getState().close();
    }
    
    @Override
    public void run() {}
    
    @Override
    public void stop() {}
}

// 關門狀态
public class ClosingState extends ElevatorState {
    @Override
    public void open() {
        super.elevator.setState(ElevatorImpl.OPENING_STATE);
        super.elevator.getState().open();
    }
    
    @Override
    public void close() {
        System.out.println("電梯關門");
    }
    
    @Override
    public void run() {
        super.elevator.setState(ElevatorImpl.RUNNING_STATE);
        super.elevator.getState().run();
    }
    
    @Override
    public void stop() {}
}

// 運作狀态
public class RunningState extends ElevatorState {
    @Override
    public void open() {}
    
    @Override
    public void close() {}
    
    @Override
    public void run() {
        System.out.println("電梯運作");
    }
    
    @Override
    public void stop() {
        super.elevator.setState(ElevatorImpl.STOPPING_STATE);
        super.elevator.getState().stop();
    }
}

// 停止狀态
public class StoppingState extends ElevatorState {
    @Override
    public void open() {
        super.elevator.setState(ElevatorImpl.OPENING_STATE);
        super.elevator.getState().open();
    }
    
    @Override
    public void close() {}
    
    @Override
    public void run() {
        super.elevator.setState(ElevatorImpl.RUNNING_STATE);
        super.elevator.getState().run();
    }
    
    @Override
    public void stop() {
        System.out.println("電梯停止");
    }
}

// 電梯實作類
public class ElevatorImpl implements Elevator {
    public final static OpeningState OPENING_STATE = new OpeningState();
    public final static ClosingState CLOSING_STATE = new ClosingState();
    public final static RunningState RUNNING_STATE = new RunningState();
    public final static StoppingState STOPPING_STATE = new StoppingState();
    
    private ElevatorState state;
    
    @Override
    public ElevatorState getState() {
        return state;
    }
    
    @Override
    public void setState(ElevatorState state) {
        this.state = state;
        this.state.setElevator(this);
    }
    
    @Override
    public void open() {
        this.state.open();
    }
    
    @Override
    public void close() {
        this.state.close();
    }
    
    @Override
    public void run() {
        this.state.run();
    }
    
    @Override
    public void stop() {
        this.state.stop();
    }
}

// 測試方法
public static void main(String[] args) {
    Elevator elevator = new ElevatorImpl();
    elevator.setState(new ClosingState());
    elevator.open();
    elevator.close();
    elevator.run();
    elevator.stop();
}
           

看,類裡面的條件判斷語句都沒有了,新增狀态也很友善。

這就是狀态模式的作用,實作狀态機的運作過程,但是卻消除了各種條件判斷,很好的解耦了狀态之間的聯系。