• 文章
  • 使用状态设计模式
作者:
发布于 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.(《设计模式》,“四人帮”)