設計模式之狀態模式(State)詳解及代碼示例


一、狀態模式的定義與特點

  應用程序中的有些對象可能會根據不同的情況做出不同的行為,我們把這種對象稱為有狀態的對象,而把影響對象行為的一個或多個動態變化的屬性稱為狀態。當有狀態的對象與外部事件產生互動時,其內部狀態會發生改變,從而使得其行為也隨之發生改變。

  狀態(State)模式的定義:對有狀態的對象,把復雜的“判斷邏輯”提取到不同的狀態對象中,允許狀態對象在其內部狀態發生改變時改變其行為。

  狀態模式的解決思想是:當控制一個對象狀態轉換的條件表達式過於復雜時,把相關“判斷邏輯”提取出來,放到一系列的狀態類當中,這樣可以把原來復雜的邏輯判斷簡單化。

二、狀態模式優缺點

  狀態模式是一種對象行為型模式,其主要優點如下:

  • 狀態模式將與特定狀態相關的行為局部化到一個狀態中,並且將不同狀態的行為分割開來,滿足“單一職責原則”。
  • 減少對象間的相互依賴。將不同的狀態引入獨立的對象中會使得狀態轉換變得更加明確,且減少對象間的相互依賴。
  • 有利於程序的擴展。通過定義新的子類很容易地增加新的狀態和轉換。

  狀態模式的主要缺點如下:

  • 狀態模式的使用必然會增加系統的類與對象的個數。
  • 狀態模式的結構與實現都較為復雜,如果使用不當會導致程序結構和代碼的混亂。

三、狀態模式的實現

  狀態模式把受環境改變的對象行為包裝在不同的狀態對象里,其意圖是讓一個對象在其內部狀態改變的時候,其行為也隨之改變。現在我們來分析其基本結構和實現方法。

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

  • 環境(Context)角色:也稱為上下文,它定義了客戶感興趣的接口,維護一個當前狀態,並將與狀態相關的操作委托給當前狀態對象來處理。
  • 抽象狀態(State)角色:定義一個接口,用以封裝環境對象中的特定狀態所對應的行為。
  • 具體狀態(Concrete    State)角色:實現抽象狀態所對應的行為。

  其結構圖如圖所示:

            

   代碼實現如下:

public class StatePatternClient
{
    public static void main(String[] args)
    {       
        Context context=new Context();    //創建環境       
        context.Handle();    //處理請求
        context.Handle();
        context.Handle();
        context.Handle();
    }
}
//環境類
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);
    }
}
//抽象狀態類
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());
    }
}

  運行結果如下:

當前狀態是 A.
當前狀態是 B.
當前狀態是 A.
當前狀態是 B.

四、狀態模式的應用實例

  用“狀態模式”設計一個學生成績的狀態轉換程序:多線程存在 5 種狀態,分別為新建狀態、就緒狀態、運行狀態、阻塞狀態和死亡狀態,各個狀態當遇到相關方法調用或事件觸發時會轉換到其他狀態,其狀態轉換規律如圖所示:

          

  現在先定義一個抽象狀態類(TheadState),然后為上圖所示的每個狀態設計一個具體狀態類,它們是新建狀態(New)、就緒狀態(Runnable )、運行狀態(Running)、阻塞狀態(Blocked)和死亡狀態(Dead),每個狀態中有觸發它們轉變狀態的方法,環境類(ThreadContext)中先生成一個初始狀態(New),並提供相關觸發方法,線程狀態轉換程序的結構圖如下圖所示:

              

  代碼如下:

public class ThreadStateTest
{
    public static void main(String[] args)
    {
        ThreadContext context=new ThreadContext();
        context.start();
        context.getCPU();
        context.suspend();
        context.resume();
        context.getCPU();
        context.stop();
    }
}
//環境類
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);
    }
}
//抽象狀態類:線程狀態
abstract class ThreadState
{
    protected String stateName; //狀態名
}
//具體狀態類:新建狀態
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("當前線程處於:死亡狀態.");   
    }   
}

  測試結果如下:

當前線程處於:新建狀態.
調用start()方法-->當前線程處於:就緒狀態.
獲得CPU時間-->當前線程處於:運行狀態.
調用suspend()方法-->當前線程處於:阻塞狀態.
調用resume()方法-->當前線程處於:就緒狀態.
獲得CPU時間-->當前線程處於:運行狀態.
調用stop()方法-->當前線程處於:死亡狀態.

五、狀態模式的應用場景

  通常在以下情況下可以考慮使用狀態模式。

  • 當一個對象的行為取決於它的狀態,並且它必須在運行時根據狀態改變它的行為時,就可以考慮使用狀態模式。
  • 一個操作中含有龐大的分支結構,並且這些分支決定於對象的狀態時。

六、狀態模式的擴展

  在有些情況下,可能有多個環境對象需要共享一組狀態,這時需要引入享元模式,將這些具體狀態對象放在集合中供程序共享,其結構圖如圖所示:

             

   共享狀態模式的不同之處是在環境類中增加了一個 HashMap 來保存相關狀態,當需要某種狀態時可以從中獲取,其程序代碼如下:

public class FlyweightStatePattern
{
    public static void main(String[] args)
    {
        ShareContext context=new ShareContext(); //創建環境       
        context.Handle(); //處理請求
        context.Handle();
        context.Handle();
        context.Handle();
    }
}
//環境類
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);
    }
}
//抽象狀態類
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"));
    }
}

  執行結果如下:

當前狀態是: 狀態1
當前狀態是: 狀態2
當前狀態是: 狀態1
當前狀態是: 狀態2


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM