所谓观察者模式,个人理解就是一对多的对象之间的关系,多个对象就是观察者,下面写一个项目中用到的类似于观察者模式。
1.写一个抽象的事件基类,暴露的函数写成虚函数,在继承该事件类中的子类中实现这些虚函数
#ifndef IEVENT_H
#define IEVENT_H
#include <QString>
class IEvent
{
public:
virtual ~IEvent(){}
virtual bool setCommand(QString cmd) = 0; //判断cmd 是否为 本对象的 执行命令
// 返回 命令是否 成功执行
//result 标记执行结果是否正确
// discription 输出结果描述
virtual bool exeCommand(bool &reslt, QString &discription) = 0;
};
#endif // IEVENT_H
2.继承抽象事件基类的观察者1:点击事件
#ifndef CLICKEVENT_H
#define CLICKEVENT_H
#include "IEvent.h"
class ClickEvent : public IEvent
{
private:
const QString EVENT_NAME = "点击";
QString command;
public:
ClickEvent();
//这两个函数必须是和继承的父类的暴露的虚函数同名同参数
bool setCommand(QString cmd);
bool exeCommand(bool &reslt, QString &discription);
};
#endif // CLICKEVENT_H
bool ClickEvent::setCommand(QString cmd)
{
// cmd 格式为:“点击 CE”
if(cmd.isEmpty())
return false;
QStringList list = cmd.split(QRegExp("\\s+"));
if(list.size() == 2 && list.at(0) == EVENT_NAME)
{
this->command = cmd;
return true;
}
else
{
return false;
}
}
bool ClickEvent::exeCommand(bool &reslt, QString &discription)
{
//需求 执行 点击 事件, 并将执行结果返回
QStringList list = command.split(QRegExp("\\s+"));
QString btnName = list.at(1);
PIX pix;
BaseInfo *bp = BaseInfo::GetInstance();
if(bp->getButtonPix(btnName, pix))
{
int sx,sy; // 分辨率
if(bp->getResolution(sx,sy))
{
mouse_event(MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE, pix.x * 65535 / sx, pix.y * 65535 / sy,0,0);
mouse_event(MOUSEEVENTF_LEFTDOWN,0,0,0,0);
Sleep(100);
mouse_event(MOUSEEVENTF_LEFTUP,0,0,0,0);
return true;
}
else
{
discription = "配置文件中,未找到屏幕分辨率信息";
return false;
}
}
else
{
discription = " 配置文件中,未找到 此按钮的 位置信息";
return false;
}
}
3.继承抽象事件基类的观察者2:判断事件
#ifndef JUDGEEVENT_H
#define JUDGEEVENT_H
#include "IEvent.h"
class JudgeEvent : public IEvent
{
private:
const QString EVENT_NAME = "期望";
QString command;
public:
JudgeEvent();
//这两个函数必须是和继承的父类的暴露的虚函数同名同参数
bool setCommand(QString cmd);
bool exeCommand(bool &reslt, QString &discription);
};
#endif // JUDGEEVENT_H
bool JudgeEvent::setCommand(QString cmd)
{
// cmd 格式为:“期望 显示框 值 等于 1005.6”
if(cmd.isEmpty())
return false;
QStringList list = cmd.split(QRegExp("\\s+"));
if(list.size() == 5 && list.at(0) == EVENT_NAME)
{
this->command = cmd;
return true;
}
else
{
return false;
}
}
bool JudgeEvent::exeCommand(bool &reslt, QString &discription)
{
//需求 执行 点击 事件, 并将执行结果返回
QStringList list = command.split(QRegExp("\\s+"));
QString btnName = list.at(BTN_NAME);
double expectValue = list.at(EXPECT_VALUE).toDouble();
AREA area;
QString filePath = "C:\\Users\\cf\\Desktop\\Qt5screen.jpg";
QString disc;
double value;
BaseInfo *bp = BaseInfo::GetInstance();
cvIdentify* cv = cvIdentify::GetInstance();
if(bp->getShowArea(btnName, area))
{
//截屏
if(cv->getScreenshotPath(area,filePath,disc))
{//数字识别
if(cv->getNumberIdentify(filePath, value))
{
if (value == expectValue)
{
qDebug() << "expect = "<< (QString::number(value, 10, 5));
discription = "执行结果与用例期望值一致的";
reslt = true;
return true;
}
else
{
qDebug() << "unexpect = " << (QString::number(value, 10, 5));
discription = "执行结果与用例期望值不一致的";
reslt = false;
return true;
}
}
else
{
discription = "识别失败";
return false;
}
}
else
{
discription = "截取数字失败";
return false;
}
//discription = "";
//return true;
}
else
{
discription = "配置文件中,未找到此显示框的位置信息";
return false;
}
}
4.写一个将两个观察者类绑定到执行器类中
#ifndef EXECUTOR_H
#define EXECUTOR_H
#include <QList>
#include <QString>
#include "IEvent.h"
class Executor
{
private:
QList<IEvent* > IEvents;
public:
Executor();
void attach(IEvent* ievent);
void detach(IEvent* ievent);
bool exeCmd(QString cmd, bool &rel, QString &des);
};
#endif // EXECUTOR_H
#include "executor.h"
#include <Windows.h>
Executor::Executor()
{
}
void Executor::attach(IEvent *ievent)
{
IEvents.push_back(ievent);
}
void Executor::detach(IEvent *ievent)
{
IEvents.removeOne(ievent);
}
// 返回 命令是否 成功执行
// cmd 要执行的 命令
// rel 标记执行结果是否正确
// dis 输出结果描述
bool Executor::exeCmd(QString cmd, bool &rel, QString &des)
{
foreach(auto event , IEvents)
{
if(event->setCommand(cmd))
{
Sleep(500);
return event->exeCommand(rel,des);
}
}
// 没有找到对应 事件
//des = " 未能找到 此命令的执行程序";
return false;
}
5.程序运行时线程中的执行函数,实现了观察者模式
void executorThread::test(const QStringList& fileName, const QVector<QStandardItem*> m_box)
{
//qDebug() << QString("my object thread id:") << QThread::currentThreadId();
//注册执行器的事件对象
if (!m_box.isEmpty())
{//这里将两个观察者绑定到执行类,根据程序运行时观察者的不同,执行不同的操作
Executor* exep = new Executor();
exep->attach(new ClickEvent());
exep->attach(new JudgeEvent());
int executCount = 0;
int passCount = 0;
for (int i = 0; i < fileName.size(); ++i)
{
QList<QPair<int, QString> > stepList;
if (importCmdFromDat(fileName.at(i), stepList))
{
emit sig_showExample(stepList);
if (m_box.at(i)->checkState())
{
bool eveOk; // 事件是否 正常执行
bool resOk = true; // 结果是否 正确
QString exeDis; // 结果的具体描述
foreach(auto step, stepList)
{
QMutexLocker locker(&m_lock);
if (isStop)
{
return;
}
resOk = true;
exeDis.clear();
eveOk = exep->exeCmd(step.second, resOk, exeDis);
emit sig_showResult(stepList,step, eveOk,exeDis);
// 对执行结果 处理, 打印, 保存
// todo
}
executCount++;
if (resOk)
{
passCount++;
}
emit sig_identifyResult(passCount,executCount,i,resOk);
//isStop = true;
}
}
}
}
}