設計模式系列—狀態模式
模式定義
對有狀態的對象,把復雜的“判斷邏輯”提取到不同的狀態對象中,允許狀態對象在其內部狀態發生改變時改變其行為。
狀態模式把受環境改變的對象行為包裝在不同的狀態對象里,其意圖是讓一個對象在其內部狀態改變的時候,其行為也隨之改變?,F在我們來分析其基本結構和實現方法。
模板實現如下:
- package com.niuh.designpattern.state.v1;
- /**
- * <p>
- * 狀態模式
- * </p>
- */
- public class StatePattern {
- public static void main(String[] args) {
- //創建環境
- Context context = new Context();
- //處理請求
- context.Handle();
- context.Handle();
- context.Handle();
- context.Handle();
- }
- }
- //抽象狀態類
- abstract class State {
- public abstract void Handle(Context context);
- }
- //具體狀態A類
- class ConcreteStateA extends State {
- public void Handle(Context context) {
- System.out.println("當前狀態是 A.");
- context.setState(new ConcreteStateB());
- }
- }
- //具體狀態B類
- class ConcreteStateB extends State {
- public void Handle(Context context) {
- System.out.println("當前狀態是 B.");
- context.setState(new ConcreteStateA());
- }
- }
- //環境類
- class Context {
- private State state;
- //定義環境類的初始狀態
- public Context() {
- this.state = new ConcreteStateA();
- }
- //設置新狀態
- public void setState(State state) {
- this.state = state;
- }
- //讀取狀態
- public State getState() {
- return (state);
- }
- //對請求做處理
- public void Handle() {
- state.Handle(this);
- }
- }
輸出結果如下:
- 當前狀態是 A.
- 當前狀態是 B.
- 當前狀態是 A.
- 當前狀態是 B.
解決的問題
對象的行為依賴于它的狀態(屬性),并且可以根據它的狀態改變而改變它的相關行為。
模式組成
實例說明
實例概況
用“狀態模式”設計一個多線程的狀態轉換程序。
分析:多線程存在 5 種狀態,分別為新建狀態、就緒狀態、運行狀態、阻塞狀態和死亡狀態,各個狀態當遇到相關方法調用或事件觸發時會轉換到其他狀態,其狀態轉換規律如下所示:
現在先定義一個抽象狀態類(TheadState),然后為上圖的每個狀態設計一個具體狀態類,它們是新建狀態(New)、就緒狀態(Runnable )、運行狀態(Running)、阻塞狀態(Blocked)和死亡狀態(Dead),每個狀態中有觸發它們轉變狀態的方法,環境類(ThreadContext)中先生成一個初始狀態(New),并提供相關觸發方法,下圖所示是線程狀態轉換程序的結構圖:
使用步驟
步驟1:定義抽象狀態類:線程狀態
- abstract class ThreadState {
- //狀態名
- protected String stateName;
- }
步驟2: 定義具體的狀態類
- //具體狀態類:新建狀態
- class New extends ThreadState {
- public New() {
- stateName = "新建狀態";
- System.out.println("當前線程處于:新建狀態.");
- }
- public void start(ThreadContext hj) {
- System.out.print("調用start()方法-->");
- if (stateName.equals("新建狀態")) {
- hj.setState(new Runnable());
- } else {
- System.out.println("當前線程不是新建狀態,不能調用start()方法.");
- }
- }
- }
- //具體狀態類:就緒狀態
- class Runnable extends ThreadState {
- public Runnable() {
- stateName = "就緒狀態";
- System.out.println("當前線程處于:就緒狀態.");
- }
- public void getCPU(ThreadContext hj) {
- System.out.print("獲得CPU時間-->");
- if (stateName.equals("就緒狀態")) {
- hj.setState(new Running());
- } else {
- System.out.println("當前線程不是就緒狀態,不能獲取CPU.");
- }
- }
- }
- //具體狀態類:運行狀態
- class Running extends ThreadState {
- public Running() {
- stateName = "運行狀態";
- System.out.println("當前線程處于:運行狀態.");
- }
- public void suspend(ThreadContext hj) {
- System.out.print("調用suspend()方法-->");
- if (stateName.equals("運行狀態")) {
- hj.setState(new Blocked());
- } else {
- System.out.println("當前線程不是運行狀態,不能調用suspend()方法.");
- }
- }
- public void stop(ThreadContext hj) {
- System.out.print("調用stop()方法-->");
- if (stateName.equals("運行狀態")) {
- hj.setState(new Dead());
- } else {
- System.out.println("當前線程不是運行狀態,不能調用stop()方法.");
- }
- }
- }
- //具體狀態類:阻塞狀態
- class Blocked extends ThreadState {
- public Blocked() {
- stateName = "阻塞狀態";
- System.out.println("當前線程處于:阻塞狀態.");
- }
- public void resume(ThreadContext hj) {
- System.out.print("調用resume()方法-->");
- if (stateName.equals("阻塞狀態")) {
- hj.setState(new Runnable());
- } else {
- System.out.println("當前線程不是阻塞狀態,不能調用resume()方法.");
- }
- }
- }
- //具體狀態類:死亡狀態
- class Dead extends ThreadState {
- public Dead() {
- stateName = "死亡狀態";
- System.out.println("當前線程處于:死亡狀態.");
- }
- }
步驟3:定義環境類
- class ThreadContext {
- private ThreadState state;
- ThreadContext() {
- state = new New();
- }
- public void setState(ThreadState state) {
- this.state = state;
- }
- public ThreadState getState() {
- return state;
- }
- public void start() {
- ((New) state).start(this);
- }
- public void getCPU() {
- ((Runnable) state).getCPU(this);
- }
- public void suspend() {
- ((Running) state).suspend(this);
- }
- public void stop() {
- ((Running) state).stop(this);
- }
- public void resume() {
- ((Blocked) state).resume(this);
- }
- }
輸出結果
- 當前線程處于:新建狀態.
- 調用start()方法-->當前線程處于:就緒狀態.
- 獲得CPU時間-->當前線程處于:運行狀態.
- 調用suspend()方法-->當前線程處于:阻塞狀態.
- 調用resume()方法-->當前線程處于:就緒狀態.
- 獲得CPU時間-->當前線程處于:運行狀態.
- 調用stop()方法-->當前線程處于:死亡狀態.
優點
- 狀態模式將與特定狀態相關的行為局部化到一個狀態中,并且將不同狀態的行為分割開來,滿足“單一職責原則”。
- 減少對象間的相互依賴。將不同的狀態引入獨立的對象中會使得狀態轉換變得更加明確,且減少對象間的相互依賴。
- 有利于程序的擴展。通過定義新的子類很容易地增加新的狀態和轉換。
缺點
- 狀態模式的使用必然會增加系統的類與對象的個數。
- 狀態模式的結構與實現都較為復雜,如果使用不當會導致程序結構和代碼的混亂。
應用場景
通常在以下情況下可以考慮使用狀態模式。
- 當一個對象的行為取決于它的狀態,并且它必須在運行時根據狀態改變它的行為時,就可以考慮使用狀態模式。
- 一個操作中含有龐大的分支結構,并且這些分支決定于對象的狀態時。
狀態模式的擴展
在有些情況下,可能有多個環境對象需要共享一組狀態,這時需要引入享元模式,將這些具體狀態對象放在集合中供程序共享,其結構圖如下:
分析:共享狀態模式的不同之處是在環境類中增加了一個 HashMap 來保存相關狀態,當需要某種狀態時可以從中獲取,其程序代碼如下:
- package com.niuh.designpattern.state.v3;
- import java.util.HashMap;
- /**
- * <p>
- * 共享狀態模式
- * </p>
- */
- public class FlyweightStatePattern {
- public static void main(String[] args) {
- //創建環境
- ShareContext context = new ShareContext();
- //處理請求
- context.Handle();
- context.Handle();
- context.Handle();
- context.Handle();
- }
- }
- //抽象狀態類
- abstract class ShareState {
- public abstract void Handle(ShareContext context);
- }
- //具體狀態1類
- class ConcreteState1 extends ShareState {
- public void Handle(ShareContext context) {
- System.out.println("當前狀態是: 狀態1");
- context.setState(context.getState("2"));
- }
- }
- //具體狀態2類
- class ConcreteState2 extends ShareState {
- public void Handle(ShareContext context) {
- System.out.println("當前狀態是: 狀態2");
- context.setState(context.getState("1"));
- }
- }
- //環境類
- class ShareContext {
- private ShareState state;
- private HashMap<String, ShareState> stateSet = new HashMap<String, ShareState>();
- public ShareContext() {
- state = new ConcreteState1();
- stateSet.put("1", state);
- state = new ConcreteState2();
- stateSet.put("2", state);
- state = getState("1");
- }
- //設置新狀態
- public void setState(ShareState state) {
- this.state = state;
- }
- //讀取狀態
- public ShareState getState(String key) {
- ShareState s = (ShareState) stateSet.get(key);
- return s;
- }
- //對請求做處理
- public void Handle() {
- state.Handle(this);
- }
- }
輸出結果如下
- 當前狀態是: 狀態1
- 當前狀態是: 狀態2
- 當前狀態是: 狀態1
- 當前狀態是: 狀態2
源碼中的應用
- #JDK中的狀態模式:
- java.util.Iterator
- # 通過FacesServlet控制, 行為取決于當前JSF生命周期的階段(狀態
- javax.faces.lifecycle.LifeCycle#execute()
PS:以上代碼提交在 Github :
https://github.com/Niuh-Study/niuh-designpatterns.git