• 文章
  • 使用狀態設計模式
作者:
釋出於 2014年12月28日(最後更新:2015年1月16日)

使用狀態設計模式解決簡單的訊號燈問題

評分:3.8/5(146票)
*****
再次使用 IDE CodeGear C++Builder 2007

  • 下載原始碼 - 6.18 KB


  • 簡介

    在本文中,我將使用良好的程式設計實踐來解釋狀態(State)設計模式的用法。本文以一個我們可以在不同時間間隔下開發的簡單問題——訊號燈——為例。



    狀態模式

    它被認為是行為型模式之一,其核心思想是允許一個物件在其內部狀態改變時改變其行為。


    這裡我們可以看到狀態模式的類圖,圖中使用了合適的類名。



    圖示解釋了各個參與者及其定義。


    • 上下文(Context)| TLNetTraffic:此類可以包含不同的狀態。

    • 狀態(State)| TLState:為所有具體狀態定義一個公共介面或抽象類,它們總是實現相同的介面或繼承自同一個抽象類。

    • 具體狀態(ConcreteState)| TL {Red, Yellow, Green}:為每個具體狀態實現與上下文某一狀態相關聯的行為。


    實現

    我們必須具備足夠且必要的面向物件程式設計(OOP)知識,尤其是對指標的理解。


    ……我們定義了上下文類(Context Class),注意,它以紅燈(Red Light)狀態啟動。


    ¨TLNetTraffic.h¨
    1
    2
    3
    4
    5
    6
    7
    8
    9
    class TLNetTraffic
    {
    	private:
    		TLState* _state;
    	public:
    		TLNetTraffic();
    		void setState ( TLState* state );
    		void Handle();
    };


    ¨TLNetTraffic.cpp¨
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    TLNetTraffic::TLNetTraffic()
    {
    	_state = new TLRed(this);
    }
    
    void TLNetTraffic::setState ( TLState* state )
    {
    	_state = state;
    }
    
    void TLNetTraffic::Handle ()
    {
    	_state->Handle();
    }


    ……所有可實現的狀態都共用的抽象類,使用 Handle() 方法作為狀態切換的介面,該方法應由派生類重寫。


    ¨TLState.h¨
    1
    2
    3
    4
    5
    class TLState
    {
      public:
    	 virtual void Handle() = 0;
    };


    ……當然,每個狀態都是獨立的。請注意,這三個狀態的實現方式是相同的,但這並非總是如此,具體取決於每個狀態需要怎樣的實現。

    ¨TLRed.h¨
    1
    2
    3
    4
    5
    6
    7
    8
    9
    class TLRed: public TLState
    {
    	private:
    		TLNetTraffic* _context;
    
    	public:
    		TLRed(TLNetTraffic* context);
    		void Handle();
    };


    ¨TLRed.cpp¨
    1
    2
    3
    4
    5
    6
    7
    TLRed::TLRed(TLNetTraffic* context): _context(context) {};
    
    void TLRed::Handle()
    {
    	printf("Red Light\n");
    	_context->setState( new TLGreen(_context) );
    }



    ¨TLGreen.h¨
    1
    2
    3
    4
    5
    6
    7
    8
    9
    class TLGreen: public TLState
    {
    	private:
    		TLNetTraffic* _context;
    
    	public:
    		TLGreen(TLNetTraffic* context);
    		void Handle();
    };


    ¨TLGreen.cpp¨
    1
    2
    3
    4
    5
    6
    7
    TLGreen::TLGreen(TLNetTraffic* context): _context(context) {};
    
    void TLGreen::Handle()
    {
    	printf("Green Light\n");
    	_context->setState( new TLYellow(_context) );
    }



    ¨TLYellow.h¨
    1
    2
    3
    4
    5
    6
    7
    8
    9
    class TLYellow: public TLState
    {
    	private:
    		TLNetTraffic* _context;
    
    	public:
    		TLYellow(TLNetTraffic* context);
    		void Handle();
    };


    ¨TLYellow.cpp¨
    1
    2
    3
    4
    5
    6
    7
    TLYellow::TLYellow(TLNetTraffic* context): _context(context) {};
    
    void TLYellow::Handle()
    {
    	printf("Yellow Light\n");
    	_context->setState( new TLRed(_context) );
    }




    ……再次是 main 函式。我們使用 <time.h> 庫來按時間間隔改變狀態。


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    int main (int argc, char* argv[])
    {
    	TLNetTraffic netTraffic;
    
    	int count = 0, i=0;
    	int seconds;
    
    	while(1)
    	{
    	   if (i%3==0) 
    		  printf("---------\nSession %d\n---------\n", ((i+1)/3)+1 );
    		   
    	   if (count == 0) seconds =6, count = 1;
    		   else if (count == 1) seconds = 4, count = 2;
    			   else if (count == 2) seconds = 5, count = 0;
    
    	   sleep( (clock_t)seconds * CLOCKS_PER_SEC );
    	   netTraffic.Handle();
    	   i++;
    	}
    	return 0;
    }



    核心思想是按照時間間隔{紅燈(6秒),綠燈(4秒),黃燈(5秒)}無限迴圈地改變燈光狀態,模擬公路上任何交通訊號燈的正常工作,其中使用的某些資料可能不準確。


    ……當然少不了 sleep() 函式,它在每次迭代中維持時間間隔。

    1
    2
    3
    4
    5
    6
    void sleep( clock_t wait )
    {
       clock_t goal;
       goal = wait + clock();
       while( goal > clock() );
    }


    將會產生如下輸出

    
    ---------
    Session 1
    ---------
    Red Light
    Green Light
    Yellow Light
    ---------
    Session 2
    ---------
    Red Light
    Green Light
    Yellow Light
    ---------
    Session 3
    ---------
    … and so on
    


    結論

    在當今所有的程式設計工作中,都應該正確使用各種反饋和已定義的設計模式。在我們的實現中,透過運用模式和技巧解決了一個問題,其能力得以體現。幸運的是,我會繼續為大家努力。



    關於設計模式的推薦書目

    • Steve Holzner, PhD. (2006). Design Patterns for Dummies.(《設計模式傻瓜書》)

    • Eric Freeman, Elizabeth Freeman, Kathy Sierra, Bert Bates. (2004). Your Brain on Design Patterns Head First Design Patterns.(《深入淺出設計模式》)

    • James W. Cooper. (2000). JAVATM Design Patterns : A Tutorial.(《Java設計模式:教程》)

    • Erich Gamma, Richard Helm, Ralph Johnson, Jhon Vlissides. (1998). Design Patterns. Gang of Four.(《設計模式》,“四人幫”)