先创建一个子线程,增加一个指针进行对象管理,线程循环调用当前状态的Process函数:
void ComThread::run()
{
ComState *curState = GetSendRequest();
while (!m_isStop) {
curState = Process(curState, GetTaskReadTest());
QThread::msleep(200);
}
}
上面代码中有两个函数GetSendRequest()和GetTaskReadTest()分别是获取状态对象实例和获取任务对象实例,这两个接口可以使用创建型设计模式进行抽象,使这个ComThread完全不依赖具体对象,只依赖抽象。
设计一个global对象,用来管理全局的ComApi对象,在main函数中就设置当前环境需要的ComApi,比如在开始我还没有写实际的win32和Linux的串口api时,先写了一个ComApiTest对象,调用ComApiTest的Send()时,就把内容放入缓存,等调用其Recv时,再把缓存中数据送出来。等到后面完成了ComApiWin32或ComApiLinux后,再在mian函数设置ComApi为ComApiWin32。
#include "global.h"
typedef struct _Global
{
Com *com;
}Global;
Global g;
void SetDefaultCom(Com *c)
{
g.com = c;
}
Com *GetDefaultCom(void)
{
return g.com;
}
Global *GetGlobal()
{
return &g;
}
#include <QCoreApplication>
#include "comthread.h"
#include "global.h"
#include "comapitest.h"
#ifdef WIN32
#include "comapi_win32.h"
#else
#include "comapi_linux.h"
#endif
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
// SetDefaultCom(BuildComApiTest());
SetDefaultCom(BuildComApi());
ComThread t;
t.start();
return a.exec();
}
目前状态有两种,一种是发请求,一种是收回复,两个状态的Process函数实现分别如下:
static
ComState* SendRequestProcess(ComState *thiz, void *ctx)
{
ComTask *comTask = (ComTask *)ctx;
unsigned char data[128] = {0};
int len = 128;
Com *comApi = GetDefaultCom();
len = CreateRequest(comTask, data, len);
if (len <= 0)
{ // 如果没有请求则下次继续发送请求
return GetSendRequest();
}
ComSend(comApi, data, len);
return GetDealRequestRecv();
}
static
ComState *DealRequestRecvProcess(ComState *thiz, void *ctx)
{
ComTask *comTask = (ComTask *)ctx;
unsigned char data[128] = {0};
int len = 128;
Com *comApi = GetDefaultCom();
len = ComRecv(comApi, data, len);
if (len <= 0)
{
return GetDealRequestRecv();
}
DealData(comTask, data, len);
return GetSendRequest();
}
可以看到状态对象中完全没有依赖具体的任务对象的,都是依赖的任务对象的抽象,也没有依赖具体的串口,也是依赖的抽象。
如果业务功能扩展,比如串口功能、通信协议、通信格式变化或扩展,可以扩展ComTask子类,如果是平台或接口功能扩展的可以扩展ComApi子类,甚至可以直接扩展一个ComApi_Tcp的子类,在其他代码完全不变的情况下改成Tcp/ip通信。如果通信步骤有变化可以扩展ComState子类,多几个状态,画状态图也是很清晰的。