解析C++编程中如何使用设计模式中的状态模式结构
作用:当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类。
UML图如下:
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++程序中使用Windows系统Native Wifi API的基本教程
本文地址:https://www.xiuzhanwang.com/a1/Cyuyan/2413.html
您可能感兴趣的文章
- 04-02c语言没有round函数 round c语言
- 01-10数据结构课程设计- 解析最少换车次数的问题详解
- 01-10深入理解C++中常见的关键字含义
- 01-10使用C++实现全排列算法的方法详解
- 01-10深入解析最长公共子串
- 01-10c++中inline的用法分析
- 01-10用C++实现DBSCAN聚类算法
- 01-10全排列算法的非递归实现与递归实现的方法(C++)
- 01-10C++大数模板(推荐)
- 01-10浅谈C/C++中的static与extern关键字的使用详解
阅读排行
本栏相关
- 04-02c语言函数调用后清空内存 c语言调用
- 04-02func函数+在C语言 func函数在c语言中
- 04-02c语言的正则匹配函数 c语言正则表达
- 04-02c语言用函数写分段 用c语言表示分段
- 04-02c语言中对数函数的表达式 c语言中对
- 04-02c语言编写函数冒泡排序 c语言冒泡排
- 04-02c语言没有round函数 round c语言
- 04-02c语言分段函数怎么求 用c语言求分段
- 04-02C语言中怎么打出三角函数 c语言中怎
- 04-02c语言调用函数求fibo C语言调用函数求
随机阅读
- 01-10C#中split用法实例总结
- 08-05dedecms(织梦)副栏目数量限制代码修改
- 08-05DEDE织梦data目录下的sessions文件夹有什
- 01-11Mac OSX 打开原生自带读写NTFS功能(图文
- 01-10delphi制作wav文件的方法
- 08-05织梦dedecms什么时候用栏目交叉功能?
- 01-11ajax实现页面的局部加载
- 01-10使用C语言求解扑克牌的顺子及n个骰子
- 04-02jquery与jsp,用jquery
- 01-10SublimeText编译C开发环境设置