狀態模式(State)的定義
定義對象間的一種一對多的依賴關系,當一個對象的狀態(對象內部的屬性,可以理解成是對象的某個字段或者方法)發生改變時,所有依賴於它的對象都得到通知並被自動更新。允許一個對象在其內部狀態改變時改變它的行為。對象看起來似乎修改了它的類
狀態模式(State)適用性
1.一個對象的行為取決於它的狀態,並且它必須在運行時刻根據狀態改變它的行為。
2.一個操作中含有龐大的多分支的條件語句,且這些分支依賴於該對象的狀態。
這個狀態通常用一個或多個枚舉常量表示。
通常,有多個操作包含這一相同的條件結構。
State模式將每一個條件分支放入一個獨立的類中。
這使得你可以根據對象自身的情況將對象的狀態作為一個對象,這一對象可以不依賴於其他對象而獨立變化。
狀態模式(State)的參與者
1.Context
定義客戶感興趣的接口。
維護一個ConcreteState子類的實例,這個實例定義當前狀態。
2.State
定義一個接口以封裝與Context的一個特定狀態相關的行為。
3.ConcreteStatesubclasses
每一子類實現一個與Context的一個狀態相關的行為。
狀態模式(State)的UML類圖
具體代碼實現:
定義State
//定義和Context中的狀態相對應的行為 public interface State { //獲取天氣情況 String getState(); }
定義Context
//定義當前的狀態 public class Context { private State state; public State getState() { return state; } public void setState(State state) { this.state = state; } public String stateMessage(){ return state.getState(); } }
定義ConcreteStatesubclasses
class Sunshine implements State{ @Override public String getState() { return "晴天"; } } class Rain implements State{ @Override public String getState() { return "下雨"; } }
測試一下
public class StateTest { public static void main(String args[]){ Context context=new Context(); context.setState(new Rain()); System.out.println(context.stateMessage()); context.setState(new Sunshine()); System.out.println(context.stateMessage()); } }
運行結果:
下雨 晴天
接下來我們用Java編程思想中的一個例子來講解一下狀態模式
我們學習了多態,看起來似乎所有的東西都可以去繼承,因為多態是一個如此巧妙的工具。事實上,當我們使用現成的類建立新類時,如果首先考慮使用繼承技術,反倒會加重我們的設計負擔,使得事情變得復雜起來。
更好的設計思想是首先選擇"組合",尤其是不能十分確定應該使用哪一種方式的時候。組合不會強制我們的程序設計進入繼承的層次結構中。而且,組合更加靈活,因為它可以動態選擇類型(因此也就選擇了行為),想法,繼承在編譯時就需要知道確定的類型,下面是具體代碼體現:
//相當於狀態模式中的state class Actor { public void act(){ } } //相當於狀態模式中的ConcreteStateSubclassess class HappyActor extends Actor{ public void act(){ System.out.println("HappyActor"); } } class SadActor extends Actor{ public void act(){ System.out.println("SadActor"); } } //相當於狀態模式中的Context class Stage{ private Actor actor=new HappyActor(); //改變引用actor的指向的具體類型 public void change(){ actor=new SadActor(); } //根據狀態的不同執行不同的行為 public void performPlay(){ actor.act(); } }
測試一下:
public class Transmogrify { /** * @param args */ public static void main(String[] args) { Stage stage=new Stage(); stage.performPlay(); stage.change(); stage.performPlay(); } }
運行結果:
HappyActor SadActor
程序分析:在這里,Stage對象包含一個對Actor的引用,而Actor被初始化為HappyActor對象,這意味着performPlay()會產生某種特殊行為。既然引用在運行時可以和另一個不同的對象重新綁定起來,SadActor對象的引用可以在actor中被替換,然后由performPlay()產生的行為也隨之改變,這樣一來,我們在運行期間獲得了動態靈活性。與此相反的是,我們不能在運行期間決定繼承不同的對象,因為它要求在編譯期間完全確定下來。
大家是不是覺得狀態模式和策略模式不是一樣的嗎?我們講一下他們之間的區別和聯系:
狀態模式和策略模式的區別和聯系(取自知乎網友的回答,很精辟):
狀態模式將各個狀態所對應的操作分離開來,即對於不同的狀態,由不同的子類實現具體操作,不同狀態的切換由子類實現,當發現傳入參數不是自己這個狀態所對應的參數,則自己給Context類切換狀態;而策略模式是直接依賴注入到Context類的參數進行選擇策略,不存在切換狀態的操作聯系。