1. 程式人生 > 其它 >【設計模式(20)】行為型模式之狀態模式

【設計模式(20)】行為型模式之狀態模式

個人學習筆記分享,當前能力有限,請勿貶低,菜鳥互學,大佬繞道

如有勘誤,歡迎指出和討論,本文後期也會進行修正和補充


人都有喜怒哀樂,在不同的情緒下,我們可能會作出不同的行為,進而影響外界事物;而外界事物也可能影響我們的情緒,導致我們作出不同的行為(完美閉環)

開發中的物件,有時候也會根據不同的情況作出不同的行為,而物件與外部事件互動的時候,也可能改變自身的狀態,進而行為也發生改變

對於有多種狀態的物件,傳統的解決方案是列舉所有的情況,使用條件判斷語句if-else或者switch-case來判斷,但對於複雜的狀態判斷,則會顯得過於臃腫和混亂。而且每次新增狀態都需要新增一個判斷,擴充套件性差

狀態模式就是為了解決這種問題而出現的,通過將狀態相關的判斷邏輯提取出來,使用不同的類進行表示,根據不同的狀態選擇相應的類來進行處理。


1.介紹

適用目的:允許物件在內部狀態發生改變時改變它的行為

主要解決:物件的行為依賴於它的狀態(屬性),並且可以根據它的狀態改變而改變它的相關行為。

何時使用:程式碼中包含大量與物件狀態有關的條件語句。

如何解決:將各種具體的狀態類抽象出來,單獨成為類。

關鍵程式碼:定義統一的介面,根據不同的狀態進行不同實現。

應用例項:系統和應用的低負載/高負載/超載狀態;安裝程式的下一步;

優點:

  • 結構清晰,狀態模式將與特定狀態相關的行為區域性化到一個狀態中,並且將不同狀態的行為分割開來,滿足“單一職責原則”。
  • 將狀態轉換顯示化,減少物件間的相互依賴。將不同的狀態引入獨立的物件中會使得狀態轉換變得更加明確,且減少物件間的相互依賴。
  • 狀態類職責明確,有利於程式的擴充套件。通過定義新的子類很容易地增加新的狀態和轉換。

缺點

  • 狀態模式的使用必然會增加系統的類與物件的個數。
  • 狀態模式的結構與實現都較為複雜,如果使用不當會導致程式結構和程式碼的混亂。
  • 狀態模式對開閉原則的支援並不太好,對於可以切換狀態的狀態模式,增加新的狀態類需要修改那些負責狀態轉換的原始碼,否則無法切換到新增狀態,而且修改某個狀態類的行為也需要修改對應類的原始碼

使用場景

  • 行為隨狀態改變而改變的場景。
  • 條件、分支語句的代替者。

2.結構

狀態模式包含以下主要角色

  • 環境類(Context):也稱為上下文,它定義了客戶端需要的介面,內部維護一個當前狀態,並負責具體狀態的切換。
  • 抽象狀態(State):定義一個介面,用以封裝環境物件中的特定狀態所對應的行為,可以有一個或多個行為。
  • 具體狀態(Concrete State):實現抽象狀態所對應的行為,並且在需要的情況下進行狀態切換。

3.步驟

  1. 建立環境類

    class Context {
        private State state;
    
        public Context(State state) {
            this.state = state;
        }
    
        public State getState() {
            return state;
        }
    
        public void setState(State state) {
            this.state = state;
        }
    
        public void handle() {
            state.handle(this);
        }
    }
    
  2. 建立抽象狀態

    interface State {
        void handle(Context context);
    }
    
  3. 建立具體狀態

    class StateA implements State {
        @Override
        public void handle(Context context) {
            System.out.println("change stateA to stateB");
            // 狀態變為B
            context.setState(new StateB());
        }
    }
    
    class StateB implements State {
        @Override
        public void handle(Context context) {
            System.out.println("change stateB to stateC");
            // 狀態變為C
            context.setState(new StateC());
        }
    }
    
    
    class StateC implements State {
        @Override
        public void handle(Context context) {
            System.out.println("change stateC to stateA");
            // 狀態變為C
            context.setState(new StateA());
        }
    }
    

測試程式碼

public class StateTest {
    public static void main(String[] args) {
        Context context = new Context(new StateA());    // 建立環境,初始狀態為A
        context.handle();   // 第1次變更,A變為B
        context.handle();   // 第2次變更,B變為C
        context.handle();   // 第3次變更,C變為A
        context.handle();   // 第4次變更,A變為B
    }
}

執行結果


4.例項

模擬場景如下

  • 分數包括三種評級
    • 不及格:小於60分
    • 及格:大於等於60且小於90
    • 優秀:大於等於90
  • 對分數進行加減,獲取當前的分數和評級

程式碼如下

package com.company.designPattern.state;

import java.util.Scanner;

public class ScoreStateTest {
    public static void main(String[] args) {
        ScoreContext account = new ScoreContext();
        Scanner input = new Scanner(System.in);
        System.out.println("學生成績狀態測試:");
        while (true) {
            System.out.print("請輸入分數變化:");
            int num = input.nextInt();
            account.add(num);
        }
    }
}

//環境類
class ScoreContext {
    private AbstractState state;

    ScoreContext() {
        state = new LowState(this);
    }

    public void setState(AbstractState state) {
        this.state = state;
    }

    public AbstractState getState() {
        return state;
    }

    public void add(int score) {
        state.addScore(score);
    }
}

//抽象狀態類
abstract class AbstractState {
    protected ScoreContext context;  //環境
    protected String stateName; //狀態名
    protected int score; //分數

    public abstract void checkState(); //檢查當前狀態

    public void addScore(int x) {
        score += x;
        System.out.print("加上:" + x + "分,\t當前分數:" + score);
        checkState();
        System.out.println("分,\t當前狀態:" + context.getState().stateName);
    }
}

//具體狀態類:不及格
class LowState extends AbstractState {
    public LowState(ScoreContext h) {
        context = h;
        stateName = "不及格";
        score = 0;
    }

    public LowState(AbstractState state) {
        context = state.context;
        stateName = "不及格";
        score = state.score;
    }

    public void checkState() {
        if (score >= 90) {
            context.setState(new HighState(this));
        } else if (score >= 60) {
            context.setState(new MiddleState(this));
        }
    }
}

//具體狀態類:中等
class MiddleState extends AbstractState {
    public MiddleState(AbstractState state) {
        context = state.context;
        stateName = "及格";
        score = state.score;
    }

    public void checkState() {
        if (score < 60) {
            context.setState(new LowState(this));
        } else if (score >= 90) {
            context.setState(new HighState(this));
        }
    }
}

//具體狀態類:優秀
class HighState extends AbstractState {
    public HighState(AbstractState state) {
        context = state.context;
        stateName = "優秀";
        score = state.score;
    }

    public void checkState() {
        if (score < 60) {
            context.setState(new LowState(this));
        } else if (score < 90) {
            context.setState(new MiddleState(this));
        }
    }
}

執行結果

後記

感覺是個很有意思的東西,非常巧妙的達到了多狀態和行為切換的目的,同時保證了高效能和低耦合


作者:Echo_Ye

WX:Echo_YeZ

Email :[email protected]

個人站點:在搭了在搭了。。。(右鍵 - 新建資料夾)