找了一篇关于 <<State Pattern>>的文章,简明易懂。
<<Understanding State Pattern in C++>>
http://www.codeproject.com/KB/architecture/StatePatternBy_Sarath__.aspx?bmkres=success
以前的工程中上位机和下位机通讯的代码,也见到过c++的状态机,但是理解起来不明了, 可能和原作者的风格有关。不如这个文章说的那么通俗。
看了demo后发现, 他的状态机没有传入状态管理指针, 状态完全是固定的变化. 如果状态节点数量或状态转换条件发生了变化, 维护起来不是很方便.
如果不传入状态管理指针, 状态响应用户请求后, 操作的数据在状态之外也无法知道.
相反, 通过将状态管理指针传入状态实例, 状态实例操作的数据和状态机的变化,都在状态管理指针中, 贴近实际应用.
在<<研磨设计模式>>中的State模式中, 给出的Demo很实用. 原版是JAVA, 我做了实验, 修改成了C++版.
/** * @file srcVoteState.cpp * <<研磨设计模式>>中 State模式 Demo的C++版 */ #include "stdafx.h" #include <windows.h> #include <tchar.h> #include <string> #include <locale.h> #include <stdlib.h> #include <stdio.h> #include "TypeDefine.h" #include "VoteManager.h" int _tmain(int argc, _TCHAR* argv[]) { INT i = 0; INT iVoteCntRc = 0; DWORD dwRc = 0; CONST TCHAR * pcUserName = _T("usr1"); CONST TCHAR * pcUserOption = _T("excellence"); std::wstring strRc; _tsetlocale(LC_ALL, _T("Chinese_People's Republic of China.936")); CVoteManager VoteManager; for(i = 0; i < 20; i++) { dwRc = VoteManager.vote((TYPE_USER_NAME)pcUserName, (TYPE_VOTE_OPTION)pcUserOption, iVoteCntRc, strRc); _tprintf(_T("用户<%s> 第[%d]次投票[%s] 投票数量[%d],")\ _T(" 投票结果[%s]\n"), pcUserName, i + 1, (RC_VOTE_OK == dwRc) ? _T("成功") : _T("失败"), iVoteCntRc, strRc.c_str()); } /** run results 用户<usr1> 第[1]次投票[成功] 投票数量[1], 投票结果[投票成功] 用户<usr1> 第[2]次投票[失败] 投票数量[2], 投票结果[请不要重复投票] 用户<usr1> 第[3]次投票[失败] 投票数量[3], 投票结果[请不要重复投票] 用户<usr1> 第[4]次投票[失败] 投票数量[4], 投票结果[请不要重复投票] 用户<usr1> 第[5]次投票[失败] 投票数量[5], 投票结果[请不要重复投票] 用户<usr1> 第[6]次投票[失败] 投票数量[6], 投票结果[恶意投票, 取消投票资格] 用户<usr1> 第[7]次投票[失败] 投票数量[7], 投票结果[恶意投票, 取消投票资格] 用户<usr1> 第[8]次投票[失败] 投票数量[8], 投票结果[恶意投票, 取消投票资格] 用户<usr1> 第[9]次投票[失败] 投票数量[8], 投票结果[黑名单用户, 取消投票资格] 用户<usr1> 第[10]次投票[失败] 投票数量[8], 投票结果[黑名单用户, 取消投票资格] 用户<usr1> 第[11]次投票[失败] 投票数量[8], 投票结果[黑名单用户, 取消投票资格] 用户<usr1> 第[12]次投票[失败] 投票数量[8], 投票结果[黑名单用户, 取消投票资格] 用户<usr1> 第[13]次投票[失败] 投票数量[8], 投票结果[黑名单用户, 取消投票资格] 用户<usr1> 第[14]次投票[失败] 投票数量[8], 投票结果[黑名单用户, 取消投票资格] 用户<usr1> 第[15]次投票[失败] 投票数量[8], 投票结果[黑名单用户, 取消投票资格] 用户<usr1> 第[16]次投票[失败] 投票数量[8], 投票结果[黑名单用户, 取消投票资格] 用户<usr1> 第[17]次投票[失败] 投票数量[8], 投票结果[黑名单用户, 取消投票资格] 用户<usr1> 第[18]次投票[失败] 投票数量[8], 投票结果[黑名单用户, 取消投票资格] 用户<usr1> 第[19]次投票[失败] 投票数量[8], 投票结果[黑名单用户, 取消投票资格] 用户<usr1> 第[20]次投票[失败] 投票数量[8], 投票结果[黑名单用户, 取消投票资格] */ getchar(); return 0; }
上位机和设备进行通讯处理时, 用状态机比较恰当. 和通讯有关的数据放到状态管理器里面, 因为通讯的状态转换比较频繁, 预先产生通讯时可能出现的N个状态机, 在程序结束的时候释放. 根据初始状态的不同,将通讯帧处理分散到对应的状态处理, 将通讯的处理解耦.
通讯时可能出现的状态有: 连接设备, 发送, 接收, 超时, 重发, 断开设备.
/** * 状态机只负责单一的状态处理 * 状态机执行结束后, 在状态管理器中, 根据执行的结果, 进行状态的转换. * 因为状态机没有责任知道下一个状态到底是啥. 否则就则加了耦合 */