二維碼
微世推網(wǎng)

掃一掃關(guān)注

當(dāng)前位置: 首頁 » 快聞?lì)^條 » 動(dòng)態(tài)資訊 » 正文

34_設(shè)計(jì)模式_狀態(tài)模式_詳解

放大字體  縮小字體 發(fā)布日期:2022-01-14 09:07:01    作者:葉昭昭    瀏覽次數(shù):172
導(dǎo)讀

在軟件開發(fā)過程中,應(yīng)用程序中得部分對象可能會(huì)根據(jù)不同得情況做出不同得行為,我們把這種對象稱為有狀態(tài)得對象,而把影響對象行為得一個(gè)或多個(gè)動(dòng)態(tài)變化得屬性稱為狀態(tài)。當(dāng)有狀態(tài)得對象與外部事件產(chǎn)生互動(dòng)時(shí),其內(nèi)部

在軟件開發(fā)過程中,應(yīng)用程序中得部分對象可能會(huì)根據(jù)不同得情況做出不同得行為,我們把這種對象稱為有狀態(tài)得對象,而把影響對象行為得一個(gè)或多個(gè)動(dòng)態(tài)變化得屬性稱為狀態(tài)。當(dāng)有狀態(tài)得對象與外部事件產(chǎn)生互動(dòng)時(shí),其內(nèi)部狀態(tài)就會(huì)發(fā)生改變,從而使其行為也發(fā)生改變。如人都有高興和傷心得時(shí)候,不同得情緒有不同得行為,當(dāng)然外界也會(huì)影響其情緒變化。

對這種有狀態(tài)得對象編程,傳統(tǒng)得解決方案是:將這些所有可能發(fā)生得情況全都考慮到,然后使用 if-else 或 switch-case 語句來做狀態(tài)判斷,再進(jìn)行不同情況得處理。但是顯然這種做法對復(fù)雜得狀態(tài)判斷存在天然弊端,條件判斷語句會(huì)過于臃腫,可讀性差,且不具備擴(kuò)展性,維護(hù)難度也大。且增加新得狀態(tài)時(shí)要添加新得 if-else 語句,這違背了“開閉原則”,不利于程序得擴(kuò)展。

以上問題如果采用“狀態(tài)模式”就能很好地得到解決。狀態(tài)模式得解決思想是:當(dāng)控制一個(gè)對象狀態(tài)轉(zhuǎn)換得條件表達(dá)式過于復(fù)雜時(shí),把相關(guān)“判斷邏輯”提取出來,用各個(gè)不同得類進(jìn)行表示,系統(tǒng)處于哪種情況,直接使用相應(yīng)得狀態(tài)類對象進(jìn)行處理,這樣能把原來復(fù)雜得邏輯判斷簡單化,消除了 if-else、switch-case 等冗余語句,代碼更有層次性,并且具備良好得擴(kuò)展力。

狀態(tài)模式得定義與特點(diǎn)

狀態(tài)(State)模式得定義:對有狀態(tài)得對象,把復(fù)雜得“判斷邏輯”提取到不同得狀態(tài)對象中,允許狀態(tài)對象在其內(nèi)部狀態(tài)發(fā)生改變時(shí)改變其行為。

狀態(tài)模式是一種對象行為型模式,其主要優(yōu)點(diǎn)如下。

  1. 結(jié)構(gòu)清晰,狀態(tài)模式將與特定狀態(tài)相關(guān)得行為局部化到一個(gè)狀態(tài)中,并且將不同狀態(tài)得行為分割開來,滿足“單一職責(zé)原則”。
  2. 將狀態(tài)轉(zhuǎn)換顯示化,減少對象間得相互依賴。將不同得狀態(tài)引入獨(dú)立得對象中會(huì)使得狀態(tài)轉(zhuǎn)換變得更加明確,且減少對象間得相互依賴。
  3. 狀態(tài)類職責(zé)明確,有利于程序得擴(kuò)展。通過定義新得子類很容易地增加新得狀態(tài)和轉(zhuǎn)換。

狀態(tài)模式得主要缺點(diǎn)如下。

  1. 狀態(tài)模式得使用必然會(huì)增加系統(tǒng)得類與對象得個(gè)數(shù)。
  2. 狀態(tài)模式得結(jié)構(gòu)與實(shí)現(xiàn)都較為復(fù)雜,如果使用不當(dāng)會(huì)導(dǎo)致程序結(jié)構(gòu)和代碼得混亂。
  3. 狀態(tài)模式對開閉原則得支持并不太好,對于可以切換狀態(tài)得狀態(tài)模式,增加新得狀態(tài)類需要修改那些負(fù)責(zé)狀態(tài)轉(zhuǎn)換得源碼,否則無法切換到新增狀態(tài),而且修改某個(gè)狀態(tài)類得行為也需要修改對應(yīng)類得源碼。
狀態(tài)模式得結(jié)構(gòu)與實(shí)現(xiàn)

狀態(tài)模式把受環(huán)境改變得對象行為包裝在不同得狀態(tài)對象里,其意圖是讓一個(gè)對象在其內(nèi)部狀態(tài)改變得時(shí)候,其行為也隨之改變?,F(xiàn)在我們來分析其基本結(jié)構(gòu)和實(shí)現(xiàn)方法。

1. 模式得結(jié)構(gòu)

狀態(tài)模式包含以下主要角色。

  1. 環(huán)境類(Context)角色:也稱為上下文,它定義了客戶端需要得接口,內(nèi)部維護(hù)一個(gè)當(dāng)前狀態(tài),并負(fù)責(zé)具體狀態(tài)得切換。
  2. 抽象狀態(tài)(State)角色:定義一個(gè)接口,用以封裝環(huán)境對象中得特定狀態(tài)所對應(yīng)得行為,可以有一個(gè)或多個(gè)行為。
  3. 具體狀態(tài)(Concrete State)角色:實(shí)現(xiàn)抽象狀態(tài)所對應(yīng)得行為,并且在需要得情況下進(jìn)行狀態(tài)切換。

其結(jié)構(gòu)圖如圖 1 所示。

圖1 狀態(tài)模式得結(jié)構(gòu)圖

2. 模式得實(shí)現(xiàn)

狀態(tài)模式得實(shí)現(xiàn)代碼如下:

public class StatePatternClient { public static void main(String[] args) { Context context = new Context(); //創(chuàng)建環(huán)境 context.Handle(); //處理請求 context.Handle(); context.Handle(); context.Handle(); }}//環(huán)境類class Context { private State state; //定義環(huán)境類得初始狀態(tài) public Context() { this.state = new ConcreteStateA(); } //設(shè)置新狀態(tài) public void setState(State state) { this.state = state; } //讀取狀態(tài) public State getState() { return (state); } //對請求做處理 public void Handle() { state.Handle(this); }}//抽象狀態(tài)類abstract class State { public abstract void Handle(Context context);}//具體狀態(tài)A類class ConcreteStateA extends State { public void Handle(Context context) { System.out.println("當(dāng)前狀態(tài)是 A."); context.setState(new ConcreteStateB()); }}//具體狀態(tài)B類class ConcreteStateB extends State { public void Handle(Context context) { System.out.println("當(dāng)前狀態(tài)是 B."); context.setState(new ConcreteStateA()); }}

程序運(yùn)行結(jié)果如下:

當(dāng)前狀態(tài)是 A.當(dāng)前狀態(tài)是 B.當(dāng)前狀態(tài)是 A.當(dāng)前狀態(tài)是 B.狀態(tài)模式得應(yīng)用實(shí)例

【例1】用“狀態(tài)模式”設(shè)計(jì)一個(gè)學(xué)生成績得狀態(tài)轉(zhuǎn)換程序。

分析:本實(shí)例包含了“不及格”“中等”和“優(yōu)秀” 3 種狀態(tài),當(dāng)學(xué)生得分?jǐn)?shù)小于 60 分時(shí)為“不及格”狀態(tài),當(dāng)分?jǐn)?shù)大于等于 60 分且小于 90 分時(shí)為“中等”狀態(tài),當(dāng)分?jǐn)?shù)大于等于 90 分時(shí)為“優(yōu)秀”狀態(tài),我們用狀態(tài)模式來實(shí)現(xiàn)這個(gè)程序。

首先,定義一個(gè)抽象狀態(tài)類(AbstractState),其中包含了環(huán)境屬性、狀態(tài)名屬性和當(dāng)前分?jǐn)?shù)屬性,以及加減分方法 addScore(intx) 和檢查當(dāng)前狀態(tài)得抽象方法 checkState()。

然后,定義“不及格”狀態(tài)類 LowState、“中等”狀態(tài)類 MiddleState 和“優(yōu)秀”狀態(tài)類 HighState,它們是具體狀態(tài)類,實(shí)現(xiàn) checkState() 方法,負(fù)責(zé)檢査自己得狀態(tài),并根據(jù)情況轉(zhuǎn)換。

蕞后,定義環(huán)境類(ScoreContext),其中包含了當(dāng)前狀態(tài)對象和加減分得方法 add(int score),客戶類通過該方法來改變成績狀態(tài)。圖 2 所示是其結(jié)構(gòu)圖。

圖2 學(xué)生成績得狀態(tài)轉(zhuǎn)換程序得結(jié)構(gòu)圖

程序代碼如下:

public class ScoreStateTest { public static void main(String[] args) { ScoreContext account = new ScoreContext(); System.out.println("學(xué)生成績狀態(tài)測試:"); account.add(30); account.add(40); account.add(25); account.add(-15); account.add(-25); }}//環(huán)境類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); }}//抽象狀態(tài)類abstract class AbstractState { protected ScoreContext hj; //環(huán)境 protected String stateName; //狀態(tài)名 protected int score; //分?jǐn)?shù) public abstract void checkState(); //檢查當(dāng)前狀態(tài) public void addScore(int x) { score += x; System.out.print("加上:" + x + "分,\t當(dāng)前分?jǐn)?shù):" + score); checkState(); System.out.println("分,\t當(dāng)前狀態(tài):" + hj.getState().stateName); }}//具體狀態(tài)類:不及格class LowState extends AbstractState { public LowState(ScoreContext h) { hj = h; stateName = "不及格"; score = 0; } public LowState(AbstractState state) { hj = state.hj; stateName = "不及格"; score = state.score; } public void checkState() { if (score >= 90) { hj.setState(new HighState(this)); } else if (score >= 60) { hj.setState(new MiddleState(this)); } }}//具體狀態(tài)類:中等class MiddleState extends AbstractState { public MiddleState(AbstractState state) { hj = state.hj; stateName = "中等"; score = state.score; } public void checkState() { if (score < 60) { hj.setState(new LowState(this)); } else if (score >= 90) { hj.setState(new HighState(this)); } }}//具體狀態(tài)類:優(yōu)秀class HighState extends AbstractState { public HighState(AbstractState state) { hj = state.hj; stateName = "優(yōu)秀"; score = state.score; } public void checkState() { if (score < 60) { hj.setState(new LowState(this)); } else if (score < 90) { hj.setState(new MiddleState(this)); } }}

程序運(yùn)行結(jié)果如下:

學(xué)生成績狀態(tài)測試:加上:30分, 當(dāng)前分?jǐn)?shù):30分, 當(dāng)前狀態(tài):不及格加上:40分, 當(dāng)前分?jǐn)?shù):70分, 當(dāng)前狀態(tài):中等加上:25分, 當(dāng)前分?jǐn)?shù):95分, 當(dāng)前狀態(tài):優(yōu)秀加上:-15分, 當(dāng)前分?jǐn)?shù):80分, 當(dāng)前狀態(tài):中等加上:-25分, 當(dāng)前分?jǐn)?shù):55分, 當(dāng)前狀態(tài):不及格

【例2】用“狀態(tài)模式”設(shè)計(jì)一個(gè)多線程得狀態(tài)轉(zhuǎn)換程序。

分析:多線程存在 5 種狀態(tài),分別為新建狀態(tài)、就緒狀態(tài)、運(yùn)行狀態(tài)、阻塞狀態(tài)和死亡狀態(tài),各個(gè)狀態(tài)當(dāng)遇到相關(guān)方法調(diào)用或事件觸發(fā)時(shí)會(huì)轉(zhuǎn)換到其他狀態(tài),其狀態(tài)轉(zhuǎn)換規(guī)律如圖 3 所示。

圖3 線程狀態(tài)轉(zhuǎn)換圖

現(xiàn)在先定義一個(gè)抽象狀態(tài)類(TheadState),然后為圖 3 所示得每個(gè)狀態(tài)設(shè)計(jì)一個(gè)具體狀態(tài)類,它們是新建狀態(tài)(New)、就緒狀態(tài)(Runnable )、運(yùn)行狀態(tài)(Running)、阻塞狀態(tài)(Blocked)和死亡狀態(tài)(Dead),每個(gè)狀態(tài)中有觸發(fā)它們轉(zhuǎn)變狀態(tài)得方法,環(huán)境類(ThreadContext)中先生成一個(gè)初始狀態(tài)(New),并提供相關(guān)觸發(fā)方法,圖 4 所示是線程狀態(tài)轉(zhuǎn)換程序得結(jié)構(gòu)圖。

圖4 線程狀態(tài)轉(zhuǎn)換程序得結(jié)構(gòu)圖

程序代碼如下:

public class ScoreStateTest { public static void main(String[] args) { ThreadContext context = new ThreadContext(); context.start(); context.getCPU(); context.suspend(); context.resume(); context.getCPU(); context.stop(); }}//環(huán)境類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); }}//抽象狀態(tài)類:線程狀態(tài)abstract class ThreadState { protected String stateName; //狀態(tài)名}//具體狀態(tài)類:新建狀態(tài)class New extends ThreadState { public New() { stateName = "新建狀態(tài)"; System.out.println("當(dāng)前線程處于:新建狀態(tài)."); } public void start(ThreadContext hj) { System.out.print("調(diào)用start()方法-->"); if (stateName.equals("新建狀態(tài)")) { hj.setState(new Runnable()); } else { System.out.println("當(dāng)前線程不是新建狀態(tài),不能調(diào)用start()方法."); } }}//具體狀態(tài)類:就緒狀態(tài)class Runnable extends ThreadState { public Runnable() { stateName = "就緒狀態(tài)"; System.out.println("當(dāng)前線程處于:就緒狀態(tài)."); } public void getCPU(ThreadContext hj) { System.out.print("獲得CPU時(shí)間-->"); if (stateName.equals("就緒狀態(tài)")) { hj.setState(new Running()); } else { System.out.println("當(dāng)前線程不是就緒狀態(tài),不能獲取CPU."); } }}//具體狀態(tài)類:運(yùn)行狀態(tài)class Running extends ThreadState { public Running() { stateName = "運(yùn)行狀態(tài)"; System.out.println("當(dāng)前線程處于:運(yùn)行狀態(tài)."); } public void suspend(ThreadContext hj) { System.out.print("調(diào)用suspend()方法-->"); if (stateName.equals("運(yùn)行狀態(tài)")) { hj.setState(new Blocked()); } else { System.out.println("當(dāng)前線程不是運(yùn)行狀態(tài),不能調(diào)用suspend()方法."); } } public void stop(ThreadContext hj) { System.out.print("調(diào)用stop()方法-->"); if (stateName.equals("運(yùn)行狀態(tài)")) { hj.setState(new Dead()); } else { System.out.println("當(dāng)前線程不是運(yùn)行狀態(tài),不能調(diào)用stop()方法."); } }}//具體狀態(tài)類:阻塞狀態(tài)class Blocked extends ThreadState { public Blocked() { stateName = "阻塞狀態(tài)"; System.out.println("當(dāng)前線程處于:阻塞狀態(tài)."); } public void resume(ThreadContext hj) { System.out.print("調(diào)用resume()方法-->"); if (stateName.equals("阻塞狀態(tài)")) { hj.setState(new Runnable()); } else { System.out.println("當(dāng)前線程不是阻塞狀態(tài),不能調(diào)用resume()方法."); } }}//具體狀態(tài)類:死亡狀態(tài)class Dead extends ThreadState { public Dead() { stateName = "死亡狀態(tài)"; System.out.println("當(dāng)前線程處于:死亡狀態(tài)."); }}

程序運(yùn)行結(jié)果如下:

當(dāng)前線程處于:新建狀態(tài).調(diào)用start()方法-->當(dāng)前線程處于:就緒狀態(tài).獲得CPU時(shí)間-->當(dāng)前線程處于:運(yùn)行狀態(tài).調(diào)用suspend()方法-->當(dāng)前線程處于:阻塞狀態(tài).調(diào)用resume()方法-->當(dāng)前線程處于:就緒狀態(tài).獲得CPU時(shí)間-->當(dāng)前線程處于:運(yùn)行狀態(tài).調(diào)用stop()方法-->當(dāng)前線程處于:死亡狀態(tài).狀態(tài)模式得應(yīng)用場景

通常在以下情況下可以考慮使用狀態(tài)模式。

  • 當(dāng)一個(gè)對象得行為取決于它得狀態(tài),并且它必須在運(yùn)行時(shí)根據(jù)狀態(tài)改變它得行為時(shí),就可以考慮使用狀態(tài)模式。
  • 一個(gè)操作中含有龐大得分支結(jié)構(gòu),并且這些分支決定于對象得狀態(tài)時(shí)。狀態(tài)模式得擴(kuò)展

    在有些情況下,可能有多個(gè)環(huán)境對象需要共享一組狀態(tài),這時(shí)需要引入享元模式,將這些具體狀態(tài)對象放在集合中供程序共享,其結(jié)構(gòu)圖如圖 5 所示。

    圖5 共享狀態(tài)模式得結(jié)構(gòu)圖

    分析:共享狀態(tài)模式得不同之處是在環(huán)境類中增加了一個(gè) HashMap 來保存相關(guān)狀態(tài),當(dāng)需要某種狀態(tài)時(shí)可以從中獲取,其程序代碼如下:

    package state;import java.util.HashMap;public class FlyweightStatePattern { public static void main(String[] args) { ShareContext context = new ShareContext(); //創(chuàng)建環(huán)境 context.Handle(); //處理請求 context.Handle(); context.Handle(); context.Handle(); }}//環(huán)境類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"); } //設(shè)置新狀態(tài) public void setState(ShareState state) { this.state = state; } //讀取狀態(tài) public ShareState getState(String key) { ShareState s = (ShareState) stateSet.get(key); return s; } //對請求做處理 public void Handle() { state.Handle(this); }}//抽象狀態(tài)類abstract class ShareState { public abstract void Handle(ShareContext context);}//具體狀態(tài)1類class ConcreteState1 extends ShareState { public void Handle(ShareContext context) { System.out.println("當(dāng)前狀態(tài)是: 狀態(tài)1"); context.setState(context.getState("2")); }}//具體狀態(tài)2類class ConcreteState2 extends ShareState { public void Handle(ShareContext context) { System.out.println("當(dāng)前狀態(tài)是: 狀態(tài)2"); context.setState(context.getState("1")); }}

    程序運(yùn)行結(jié)果如下:

    當(dāng)前狀態(tài)是: 狀態(tài)1當(dāng)前狀態(tài)是: 狀態(tài)2當(dāng)前狀態(tài)是: 狀態(tài)1當(dāng)前狀態(tài)是: 狀態(tài)2拓展?fàn)顟B(tài)模式與責(zé)任鏈模式得區(qū)別

    狀態(tài)模式和責(zé)任鏈模式都能消除 if-else 分支過多得問題。但在某些情況下,狀態(tài)模式中得狀態(tài)可以理解為責(zé)任,那么在這種情況下,兩種模式都可以使用。

    從定義來看,狀態(tài)模式強(qiáng)調(diào)得是一個(gè)對象內(nèi)在狀態(tài)得改變,而責(zé)任鏈模式強(qiáng)調(diào)得是外部節(jié)點(diǎn)對象間得改變。

    從代碼實(shí)現(xiàn)上來看,兩者蕞大得區(qū)別就是狀態(tài)模式得各個(gè)狀態(tài)對象知道自己要進(jìn)入得下一個(gè)狀態(tài)對象,而責(zé)任鏈模式并不清楚其下一個(gè)節(jié)點(diǎn)處理對象,因?yàn)殒準(zhǔn)浇M裝由客戶端負(fù)責(zé)。

    狀態(tài)模式與策略模式得區(qū)別

    狀態(tài)模式和策略模式得 UML 類圖架構(gòu)幾乎完全一樣,但兩者得應(yīng)用場景是不一樣得。策略模式得多種算法行為擇其一都能滿足,彼此之間是獨(dú)立得,用戶可自行更換策略算法,而狀態(tài)模式得各個(gè)狀態(tài)間存在相互關(guān)系,彼此之間在一定條件下存在自動(dòng)切換狀態(tài)得效果,并且用戶無法指定狀態(tài),只能設(shè)置初始狀態(tài)。

  •  
    (文/葉昭昭)
    免責(zé)聲明
    本文為葉昭昭原創(chuàng)作品?作者: 葉昭昭。歡迎轉(zhuǎn)載,轉(zhuǎn)載請注明原文出處:http://m.jib360.com/news/show-244894.html 。本文僅代表作者個(gè)人觀點(diǎn),本站未對其內(nèi)容進(jìn)行核實(shí),請讀者僅做參考,如若文中涉及有違公德、觸犯法律的內(nèi)容,一經(jīng)發(fā)現(xiàn),立即刪除,作者需自行承擔(dān)相應(yīng)責(zé)任。涉及到版權(quán)或其他問題,請及時(shí)聯(lián)系我們郵件:weilaitui@qq.com。
     

    Copyright?2015-2023 粵公網(wǎng)安備 44030702000869號(hào)

    粵ICP備16078936號(hào)

    微信

    關(guān)注
    微信

    微信二維碼

    WAP二維碼

    客服

    聯(lián)系
    客服

    聯(lián)系客服:

    24在線QQ: 770665880

    客服電話: 020-82301567

    E_mail郵箱: weilaitui@qq.com

    微信公眾號(hào): weishitui

    韓瑞 小英 張澤

    工作時(shí)間:

    周一至周五: 08:00 - 24:00

    反饋

    用戶
    反饋