解析C++编程中如何使用设计模式中的状态模式结构

所属分类: 软件编程 / C 语言 阅读数: 48
收藏 0 赞 0 分享

作用:当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类。

UML图如下:

2016316112712572.png (609×389)

State类,抽象状态类,定义一个接口以封装与Context的一个特定状态相关的行为。
ConcreteState类,具体状态,每一个子类实现一个与Context的一个状态相关的行为。
Context类,维护一个ConcreteState子类的实例,这个实例定义当前的状态。

状态模式主要解决的是当控制一个对象状态转换的条件表达式过于复杂时的情况。把状态的判断逻辑转移到表示不同状态的一系列类当中,可以把复杂的判断逻辑简化。

状态模式的好处是将与特定状态相关的行为局部化,并且将不同状态的行为分割开来。

将特定的状态相关的行为都放入一个对象中,由于所有与状态相关的代码都存在于某个ConcreteState中,所以通过定义新的子类可以很容易地增加新的状态和转换。

可以消除庞大的条件分支语句。状态模式通过把各种状态转移逻辑分布到State的子类之间,来减少相互间的依赖。

当一个对象的行为取决于它的状态,并且它必须在运行时刻根据状态改变它的行为时,就可以考虑使用状态模式。
另外如果业务需求某项业务有多个状态,通常都是一些枚举常量,状态的变化都是依靠大量的多分支判断语句来实现,此时应该考虑将每一种业务状态定义为一个State的子类。这样这些对象就可以不依赖于其他对象儿独立变化了。

实例代码如下:

State.h#ifndef _STATE_H_

#define _STATE_H_

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

class ConcreteStateA : public State
{
public:
  ConcreteStateA();
  ~ConcreteStateA();
  virtual void Handle(Context* pContext);
protected:
private:
};

class ConcreteStateB : public State
{
public:
  ConcreteStateB();
  ~ConcreteStateB();
  virtual void Handle(Context* pContext);
protected:
private:
};

class ConcreteStateC : public State
{
public:
  ConcreteStateC();
  ~ConcreteStateC();
  virtual void Handle(Context* pContext);
protected:
private:
};

class Context
{
public:
  Context(State* pState);
  ~Context();
  void Request();
  void ChangeState(State* pState);
protected:
private:
  State* _state;
};


#endif


State.cpp

#include "State.h"
#include <iostream>

using namespace std;

State::State()
{}

State::~State()
{}

ConcreteStateA::ConcreteStateA()
{}

ConcreteStateA::~ConcreteStateA()
{}

//执行该状态的行为并改变状态
void ConcreteStateA::Handle(Context* pContext)
{
  cout << "ConcreteStateA" << endl;
  pContext->ChangeState(new ConcreteStateB());
}

ConcreteStateB::ConcreteStateB()
{}

ConcreteStateB::~ConcreteStateB()
{}

//执行该状态的行为并改变状态
void ConcreteStateB::Handle(Context* pContext)
{
  cout << "ConcreteStateB" << endl;
  pContext->ChangeState(new ConcreteStateC());
}

ConcreteStateC::ConcreteStateC()
{}

ConcreteStateC::~ConcreteStateC()
{}

//执行该状态的行为并改变状态
void ConcreteStateC::Handle(Context* pContext)
{
  cout << "ConcreteStateC" << endl;
  pContext->ChangeState(new ConcreteStateA());
}

//定义_state的初始状态
Context::Context(State* pState)
{
  this->_state = pState;
}

Context::~Context()
{}

//对请求做处理,并设置下一状态
void Context::Request()
{
  if(NULL != this->_state)
  {
    this->_state->Handle(this);
  }
}

//改变状态
void Context::ChangeState(State* pState)
{
  this->_state = pState;
}

main.cpp
#include "State.h"

int main()
{
  State* pState = new ConcreteStateA();
  Context* pContext = new Context(pState);
  pContext->Request();
  pContext->Request();
  pContext->Request();
  pContext->Request();
  pContext->Request();
  return 0;
}

总结

对于状态模式,很多情况下和策略模式看起来极为相似。实际上它们都是为了解决具体子类实现抽象接口的实现异构问题而存在的(封装变化),但是它们的侧重各不相同。而针对算法的异构问题,模板方法模式通过继承的方式来改变一部分算法实现(原子操作在不同具体子类中可以有不同实现),策略模式则通过组合的方式来改变整个算法(可动态替换),而状态模式则强调的是针对不同的状态对象可以有不同的响应。因此状态模式实际上强调的状态的概念,并且强调对状态转换的逻辑封装,即对象可能处于不同的状态下,而各个状态在响应了该状态的实现后可能会动态转到另一个状态,而这个转变我们不希望 Context 的参与(Context 不必维护这个转换)。状态机在编译原理的 DFA/NDFA 中很常见,针对一个输入字符和已有串,DFA/NDFA 可能会转换到另外一个状态。

因此对于状态模式有以下几个关键点:

1. 状态模式会处理算法的不同,但是更加关注的是状态的改变。并且对于状态的转变逻辑一般会放在 State 子类中实现。而对于不同状态的处理则可以放在 Context 类中,State 子类保存一个指向 Context 的引用(实际上往往传递一个指向 Context 的指针即可,而不必在 State 子类真正保存一个引用),以调用这些实现。当然放在 State 子类中实现也无可厚非,不过为了突出重点,使用前一种方式实现更能说明问题。当然在实际开发中,完全可以不受这个制约。

2.在具体实现过程中,对状态的改变我们会在 Context 类中实现(因为Context 才有 State 的概念),而在 State 子类中的状态转变逻辑实现则通过调用这个实现来达到目的。当然为了不让这个改变状态的接口暴露给普通客户程序员,我们将 Context 中这个接口声明为 private,而在将State 类声明为 Context 的 friend 类,并且将 State 子类中状态改变逻辑实现声明为 Protected,不让普通客户程序员调用。具体请参考示例代码部分。

更多精彩内容其他人还在看

C++广播通信实例

这篇文章主要介绍了C++实现广播通信的方法,实例讲述了C++ socket广播通信的原理与实现方法,需要的朋友可以参考下
收藏 0 赞 0 分享

C++计算ICMP头的校验和实例

这篇文章主要介绍了C++计算ICMP头的校验和的方法,代码简单实用,对于校验ICMP报文来说有不错的实用价值,需要的朋友可以参考下
收藏 0 赞 0 分享

C++设置超时时间的简单实现方法

这篇文章主要介绍了C++设置超时时间的简单实现方法,涉及系统函数setsockopt对套接口的操作,具有一定的实用价值,需要的朋友可以参考下
收藏 0 赞 0 分享

C++实现ping程序实例

这篇文章主要介绍了C++实现ping程序实例,涉及C++对于ICMP数据包的发送与回显处理,具有一定的实用价值,需要的朋友可以参考下
收藏 0 赞 0 分享

C++之boost::array的用法

这篇文章主要介绍了C++之boost::array的用法,以实例的形式简单讲述了静态数组的容器boost::array的使用技巧,具有一定的参考借鉴价值,需要的朋友可以参考下
收藏 0 赞 0 分享

C++之Boost::array用法简介

这篇文章主要介绍了C++之Boost::array用法简介,较为详细的分析了Boost::array中的常见用法,并用实例的形式予以总结归纳,需要的朋友可以参考下
收藏 0 赞 0 分享

VC文件目录常见操作实例汇总

这篇文章主要介绍了VC文件目录常见操作实例汇总,总结了VC针对文件目录的各种常用操作,非常具有实用价值,需要的朋友可以参考下
收藏 0 赞 0 分享

VC打印word,excel文本文件的方法

这篇文章主要介绍了VC打印word,excel文本文件的方法,是VC操作文本文件中非常实用的技巧,需要的朋友可以参考下
收藏 0 赞 0 分享

VC++获得当前进程运行目录的方法

这篇文章主要介绍了VC++获得当前进程运行目录的方法,可通过系统函数实现该功能,是非常实用的技巧,需要的朋友可以参考下
收藏 0 赞 0 分享

VC中SendMessage和PostMessage的区别

这篇文章主要介绍了VC中SendMessage和PostMessage的区别,较为全面的分析了SendMessage和PostMessage运行原理及用法上的不同之处,非常具有实用价值,需要的朋友可以参考下
收藏 0 赞 0 分享
查看更多