大卫的Design Patterns学习笔记20:State

一、概述
State(状态)模式用于把一个对象的内部状态从对象中分离出来,形成单独的状态对象,所有与该状态相关的行为都放入该状态对象中。
一个对象可能处在这样或者那样的状态,并且在不同的状态下会表现出不同的行为,这是很平常的事情,例如,我们制作一个定点报时程序,当时间是 0 : 00 - 12 : 00时,问候语是:“Good Morning ! 现在是 ...AM .”;当时间是 12 : 00 - 18 : 00时,问候语是:“Good Afternoon ! 现在是 ...PM .”,其它时段,问候语是:“Good Evening ! 现在是 ...PM .”,这只是一个简化的例子,你可以采用多种方式解决这个问题。
如以下是一种典型的实现:
void
 Alarm ::Action ()
{

    int
 i  = m_currentClock  /  12 ;
    if
 (  0 == i  )
    {

        // Good Morning!
    }
    else

    {

        i  = m_currentClock  /  6 ;
        if
 (  2  == i )
        {

            // Good Afternoon!
        }
        else

        {

            // Good Evening!
        }
    }
}

看到这里,你可能会想:这样处理挺简单的呀,我就不信你用State模式的思想处理能比这更简单!
确实,对于上面的问题,由于十分简单,使用State模式并不能使解决它变得更加简单,但当需要执行的操作并非仅Action一个,而是多个,如Action1、Action2、 ....,上面的解决方案就会表现出一个明显的问题:所有的逻辑判断都必须被重复,任何“差池”都可能给我们的系统带来不易察觉的Bug,同时,维护也变得困难,任何需求的变更,如,我们要将 11 : 00 - 12 : 00这个时间段的问候语改为:“Good Noon ! 现在是 ...”,所有的逻辑判断部分都需要修改 ......
我们现在用State的观点来考虑这个问题,假定报时由Alarm类完成,Alarm对象在不同的时段处于不同的状态(Morning /Afternoon /Evening),为此,我们添加一个State基类,它的子类负责实现相应的报时动作。以上解决方案使得我们在整个系统运行的过程中,只需要维护状态的变化,而不是像前面采用 if ... else语句一样将所有的状态下的需要执行的行为混杂在一起,单纯依靠属性判断来决定执行何种动作。后面的举例部分将给出该问题的State模式实现。
State模式是OOD中用于替代 if ... else / switch的一种方式(当然,不仅限于此,应用部分对此有进一步讨论),State模式的应用使得对象被划分成对象  + 状态对象两部分,一定程度上体现了Delegate的设计原则。
在此需要注意的是,这里的State(状态)并非仅仅指对象当前所具有的属性,还包括对对象在拥有当前属性时行为的封装,这从类设计的角度讲是很自然的,因为类除了应该包括一定的属性信息,还应该包含操作这些属性的方法,及这些属性对应的行为。

二、结构
State模式的结构如下图所示:

1、State模式类图示意
上述类图中包括如下角色:
Context(环境):负责定义客户感兴趣的接口,并维护一个ConcreteState的实例,以及维护状态转换或为这种转换提供支持(因为状态转换有时候可能是由上层应用发起的)。
State(状态):定义一个接口以封装与Context的一个特定状态相关的行为。
ConcreteState(具体状态):每一子类实现一个与Context的一个状态相关的行为。

三、应用
根据概述部分的讨论,可以看出,State模式适用于解决以下的问题:
1.
一个对象的行为取决于它的状态 , 并且它必须在运行时刻根据状态改变它的行为。
2.
一个操作中含有庞大的多分支的条件语句,且这些分支依赖于该对象的状态。这个状态通常用一个或多个枚举常量表示。通常 , 有多个操作包含这一相同的条件结构。State模式将每一个条件分支放入一个独立的类中。这使得你可以根据对象自身的情况将对象的状态作为一个对象,这一对象可以不依赖于其他对象而独立变化。

四、优缺点
State模式具有以下优点:
避免了为判断状态而产生的巨大的 ifcase语句。
将对象行为交给状态类维护后,对于上层程序而言,仅需要维护状态之间的转换规则。
并且,当状态信息发生增减时,维护成本比较低,仅需根据增减情况调整State子类的数目,并对状态转换逻辑进行简单调整,而不是陷入维护众多 if ... else逻辑判断的危险境地。

五、举例
运用State模式时存在一些细节问题需要进一步深入考虑,如:State类可能需要操作Context类的某些保护 /私有成员以完成相应动作的执行,那么State类应该设置成Context类的 friend类?或者内嵌类?或者其他形式?这里考虑到State扩展的需要,一般不会将State类设置成Context类的友元类(由于友元特性无法继承,主要是将State类的各子类设置成Context类的友元类),因为,不但所有State类都必须设置成Context的友元类,当需求发生变更,State发生增减时,都需要维护友元类信息。鉴于此,内嵌类是个不错的选择,但也并非唯一选择。
此外,State类是否需要维护一个Context类的指针等问题,也需要在具体实现时根据需要来确定,在此不再赘述。
下面给出概述中所讨论的定点报时问题的State模式解决方案:
#include <iostream>
using namespace std ;

struct
 AlarmState
{

    virtual
 ~AlarmState () { }

    virtual
 void doAlarm () =  0 ;
    virtual
 void changeState (AlarmState ** ppState )
    {

        if
 (NULL  != *ppState )
            delete
 *ppState ;

        *
ppState  = NULL ;
        cout  <<  "deleting..."  << endl ;
    }
};


class
 MorningState  :  public AlarmState
{

public
:
    void
 doAlarm ()
    {

        cout  <<  "Good Morning!"  << endl ;
    }


    void
 changeState (AlarmState ** ppState );
};


class
 NoonState  :  public AlarmState
{

public
:
    void
 doAlarm ()
    {

        cout  <<  "Good Noon!"  << endl ;
    }


    void
 changeState (AlarmState ** ppState );
};


class
 AfternoonState  :  public AlarmState
{

public
:
    void
 doAlarm ()
    {

        cout  <<  "Good Afternoon!"  << endl ;
    }


    void
 changeState (AlarmState ** ppState );
};


class
 EveningState  :  public AlarmState
{

public
:
    void
 doAlarm ()
    {

        cout  <<  "Good Evening!"  << endl ;
    }


    void
 changeState (AlarmState ** ppState );
};


// To avoid problem, we seperate the implementation from the declaration.
// Otherwise, we must face a dilemma of the cyclic reference of ConcreteState
// classes.
void MorningState ::changeState (AlarmState ** ppState )
{

    AlarmState ::changeState (ppState );
    *
ppState  =  new NoonState ;
}


void
 NoonState ::changeState (AlarmState ** ppState )
{

    AlarmState ::changeState (ppState );
    *
ppState  =  new AfternoonState ;
}


void
 AfternoonState ::changeState (AlarmState ** ppState )
{

    AlarmState ::changeState (ppState );
    *
ppState  =  new EveningState ;
}


void
 EveningState ::changeState (AlarmState ** ppState )
{

    AlarmState ::changeState (ppState );
    *
ppState  =  new MorningState ;
}


class
 Alarm
{

    AlarmState * pState ;
public
:
    Alarm ()
    {

        pState  =  new MorningState ;
    }
    
    ~
Alarm ()
    {

        if
 (pState  != NULL )
            delete
 pState ;
    }


    void
 doAlarm ()
    {

        pState ->doAlarm ();
    }


    void
 changeState ()
    {

        pState ->changeState (&pState );
    }
};


int
 main ()
{

    Alarm a ;
    a .doAlarm ();

    // time elapses

    a .changeState ();
    a .doAlarm ();

    //...

    return
 0 ;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
现代C++中的设计模式是用于对象重用的可重复性方法。设计模式是一种在不同情况下解决相似问题的经验总结,可以通过将问题解决方案的关键部分抽象出来,从而提供灵活性和可重用性。设计模式不是编程语言特定的功能,而是一种通用的方法论。 在现代C++中,有许多常用的设计模式可以用于对象的可重用性。以下是几个常见的设计模式示例: 1.单例模式:用于确保一个类只能创建一个实例,并提供对该实例的全局访问点。对于有些对象只需要一个实例的情况,单例模式可以确保该实例的唯一性,从而方便访问和管理。 2.工厂模式:用于创建对象的过程中封装创建逻辑,让客户端代码无需关心对象的具体创建细节。通过工厂模式,可以通过一个工厂类来创建对象,从而提供更高的灵活性和可扩展性。 3.观察者模式:用于对象之间的发布-订阅机制,让一个对象(主题)的状态发生变化时,能够通知并自动更新其他依赖于该对象的对象(观察者)。通过观察者模式,可以实现对象之间的松耦合和消息传递,提高对象的可重用性和可维护性。 4.适配器模式:用于将一个类的接口转换成客户端所期望的另一个接口。适配器模式可以解决接口不兼容的问题,从而使得原本不兼容的类能够一起工作,提高可重用性和互操作性。 5.策略模式:用于定义一系列算法/行为,并将其封装成独立的类,使得它们可以互相替换。策略模式可以在运行时根据需要动态切换算法/行为,从而提供更高的灵活性和可重用性。 这些设计模式都是在现代C++中常见且有用的重用性方法,可以根据具体的应用场景选择合适的设计模式来提高代码的可维护性、可扩展性和可重用性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值