zoukankan      html  css  js  c++  java
  • C++设计模式——状态模式

    前言

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

    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
         }
    }

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

     

    状态模式

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

     

    UML类图

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

    它们之间的协作步骤如下:

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

     

    使用场合

    在以下两种情况下均可使用State模式:

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

     

    代码实现

     1 #include <iostream>
     2 using namespace std;
     3 
     4 class Context;
     5 
     6 class State
     7 {
     8 public:
     9      virtual void Handle(Context *pContext) = 0;
    10 };
    11 
    12 class ConcreteStateA : public State
    13 {
    14 public:
    15      virtual void Handle(Context *pContext)
    16      {
    17           cout<<"I am concretestateA."<<endl;
    18      }
    19 };
    20 
    21 class ConcreteStateB : public State
    22 {
    23 public:
    24      virtual void Handle(Context *pContext)
    25      {
    26           cout<<"I am concretestateB."<<endl;
    27      }
    28 };
    29 
    30 class Context
    31 {
    32 public:
    33      Context(State *pState) : m_pState(pState){}
    34 
    35      void Request()
    36      {
    37           if (m_pState)
    38           {
    39                m_pState->Handle(this);
    40           }
    41      }
    42 
    43      void ChangeState(State *pState)
    44      {
    45           m_pState = pState;
    46      }
    47 
    48 private:
    49      State *m_pState;
    50 };
    51 
    52 int main()
    53 {
    54      State *pStateA = new ConcreteStateA();
    55      State *pStateB = new ConcreteStateB();
    56      Context *pContext = new Context(pStateA);
    57      pContext->Request();
    58 
    59      pContext->ChangeState(pStateB);
    60      pContext->Request();
    61 
    62      delete pContext;
    63      delete pStateB;
    64      delete pStateA;
    65 }

    总结

    状态模式总的来说是非常好理解的;没有多么深奥的时序关系,就是简单的将对象的状态和对应状态下的行为分离开来,不再是简单的if…else或switch…case分支结构了,而是每一个状态都对应一个类,一个类集中管理一个状态;在多状态的情况下,简化了程序的维护和管理,让程序结构简明化,同时也易于扩展。

  • 相关阅读:
    IDEA 2017 安装和破解
    Linux新增开放端口
    ping指定IP的指定端口号
    Ubuntu防火墙常用命令
    Ubuntu端口常用命令
    File Zilla连接Ubuntu 失败
    Docker常用命令详解
    Linux的vi常用命令详解
    Qt事件系统之二:鼠标事件和滚轮事件
    Qt事件系统之一:Qt中的事件处理与传递
  • 原文地址:https://www.cnblogs.com/ring1992/p/9593533.html
Copyright © 2011-2022 走看看