[设计模式]State状态模式

前言

在实际开发中,我们经常会遇到这种情况;一个对象有多种状态,在每一个状态下,都会有不同的行为。那么在代码中我们经常是这样实现的。

typedef enum tagState
{
     state,
     state1,
     state2
}State;
void Action(State actionState)
{
     if (actionState == state)
     {
          // DoSomething
     }
     else if (actionState == state1)
     {
          // DoSomething
     }
     else if (actionState == state2)
     {
          // DoSomething
     }
     else
     {
          // DoSomething
     }
}

而这种就好比简单工厂模式,当我们增加新的状态类型时,我们又需要修改原来的代码,这种对于测试是很不利的;由于简单工厂的缺点那么的明显,后来的工厂模式就克服了这个缺点,我们就可以借鉴工长模式,来解决这种随着状态增加而出现的多分支结构。

state状态模式

在GOF的《设计模式:可复用面向对象软件的基础》一书中对状态模式是这样说的:允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它的类。状态模式的重点在于状态转换,很多时候,对于一个对象的状态,我们都是让这个对象包含一个状态的属性,这个状态属性记录着对象的具体状态,根据状态的不同使用分支结构来执行不同的功能,就像上面的代码那样处理;就像上面说的,类中存在大量的结构类似的分支语句,变得难以维护和理解。状态模式消除了分支语句,就像工厂模式消除了简单工厂模式的分支语句一样,将状态处理分散到各个状态子类中去,每个子类集中处理一种状态,这样就使得状态的处理和转换清晰明确。或者说,每个状态有着相应的行为.

使用场合

State模式在实际使用中比较多,适合"状态的切换".因为我们经常会使用If elseif else 进行状态切换, 如果针对状态的这样判断切换反复出现,我们就要联想到是否可以采取State模式了.

不只是根据状态,也有根据属性.如果某个对象的属性不同,对象的行为就不一样,这点在数据库系统中出现频率比较高,我们经常会在一个数据表的尾部,加上property属性含义的字段,用以标识记录中一些特殊性质的记录,这种属性的改变(切换)又是随时可能发生的,就有可能要使用State.在以下两种情况下均可使用State模式:

  • 一个对象的行为取决于它的状态,并且它必须在运行时刻根据状态改变它的行为;
  • 一个操作中含有庞大的多分支的条件语句,且这些分支依赖于该对象的状态。这个状态通常用一个或多个枚举常量表示。通常有多个操作包含这一相同的条件结构。State模式将每一个条件分支放入一个独立的类中。这使得你可以根据对象自身的情况将对象的状态作为一个对象,这一对象可以不依赖于其它对象而独立变化。

是否使用?
在实际使用,类似开关一样的状态切换是很多的,但有时并不是那么明显,取决于你的经验和对系统的理解深度.
这里要阐述的是"开关切换状态" 和" 一般的状态判断"是有一些区别的, " 一般的状态判断"也是有 if..elseif结构,例如:
    if (which==1) state="hello";
    else if (which==2) state="hi";
    else if (which==3) state="bye";
这是一个 " 一般的状态判断",state值的不同是根据which变量来决定的,which和state没有关系.如果改成:
    if (state.euqals("bye")) state="hello";
    else if (state.euqals("hello")) state="hi";
    else if (state.euqals("hi")) state="bye";
这就是 "开关切换状态",是将state的状态从"hello"切换到"hi",再切换到""bye";在切换到"hello",好象一个旋转开关,这种状态改变就可以使用State模式了.
如果单纯有上面一种将"hello"-->"hi"-->"bye"-->"hello"这一个方向切换,也不一定需要使用State模式,因为State模式会建立很多子类,复杂化,但是如果又发生另外一个行为:将上面的切换方向反过来切换,或者需要任意切换,就需要State了.

当状态数目不是很多的时候,switch/case可能可以搞定,但是当数目很多的时候(实际系统中也正是如此),维护一大组的switch/case语句将是一件异常困难并且出差的事情。状态逻辑和动作实现没有分离。在很多的系统实现中,动作的实现代码直接写在状态的逻辑当中。这带来的后果就是系统的扩展性和维护得不到保证。

state模式就是被用来解决上面列出的两个问题,state模式中我们将状态逻辑和动作实现进行分离。当一个操作中要维护大量的case分支语句,并且这些分支依赖对象的状态。state模式将每一个分支都封装到独立的类中。

UML类图

允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它所属的类。

Context:定义客户端感兴趣的接口,并且维护一个ConcreteState子类的实例,这个实例定义当前状态
State:定义一个接口以封装与Context的一个特定状态相关的行为
ConcreteState subclasses:每一个子类实现一个与Context的一个状态相关的行为。
它们之间的协作步骤如下:

  • Context将与状态相关的请求委托给当前的ConcreteState对象处理;
  • Context可以将自身作为一个参数传递给处理该请求的状态对象。这使得状态对象在必要时可以访问Context;
  • Context是客户使用的主要接口。客户可用状态对象来配置一个Context,一旦一个Context配置完毕,它的客户不再需要直接与状态对象打交道;

state模式采用了对这些不同状态进行封装的方式处理这类问题,当状态改变的时候进行处理然后再切换到另一种状态,也就是说白状态的切换责任交给了具体的状态去负责。

state模式和strategy模式在图示上有很多相似的地方。需要说明的是两者的思想都是一致的。只不过封装的东西不同:state模式封装的是不同的状态,而strategy模式封装的是不同的算法。

#include <iostream>
using namespace std;

class Context;

class State
{
public:
     virtual void Handle(Context *pContext) = 0;
};

class ConcreteStateA : public State
{
public:
     virtual void Handle(Context *pContext)
     {
          cout<<"I am concretestateA."<<endl;
     }
};

class ConcreteStateB : public State
{
public:
     virtual void Handle(Context *pContext)
     {
          cout<<"I am concretestateB."<<endl;
     }
};

class Context
{
public:
     Context(State *pState) : m_pState(pState){}

     void Request()
     {
          if (m_pState)
          {
               m_pState->Handle(this);
          }
     }

     void ChangeState(State *pState)
     {
          m_pState = pState;
     }

private:
     State *m_pState;
};

int main()
{
     State *pStateA = new ConcreteStateA();
     State *pStateB = new ConcreteStateB();
     Context *pContext = new Context(pStateA);
     pContext->Request();

     pContext->ChangeState(pStateB);
     pContext->Request();

     delete pContext;
     delete pStateB;
     delete pStateA;
}

state及其子类中的操作都将Context*传入作为参数,其主要目的是state类可以通过这个指针调用Context中的方法这也是state模式和strategy模式最大的区别。

state模式和strategy模式的区别在于它们所关注的点不尽相同:state模式主要是要适应对象对于状态改变时的不同处理策略的实现,而strategy则主要是具体算法和实现接口的解耦strategy模式中并没有状态的概念(虽然很多时候有可以被看作是状态的概念),并且更加不关心状态的改变。

state模式很好的实现了对象的状态逻辑和动作实现的分离状态逻辑分布在state的派生类中实现,而动作实现则可以放在context类中实现(这也是为什么state派生类需要拥有一个指向context的指针)。这使得两者的变化相互独立,改变state的状态逻辑可以很容易复用context的动作,也可以不影响state派生类的前提下创建context的子类来更改或替换动作实现。

state模式问题主要是逻辑分散化,状态逻辑分布到了很多的state的子类中,很难看到整个的状态逻辑图,这也带来了代码的维护问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值