命令模式是編程設(shè)計(jì)模式中的一種耻台,這里介紹命令模式在Qt網(wǎng)絡(luò)編程中的使用,講述如何實(shí)現(xiàn)“一個(gè)類(lèi)就是一個(gè)命令”的設(shè)計(jì)思想。
基本思想
把向服務(wù)器發(fā)起的請(qǐng)求抽象為一個(gè)C++類(lèi)作烟,相似的請(qǐng)求可以封裝為同一個(gè)類(lèi)部翘,通過(guò)操作類(lèi)型來(lái)區(qū)分。(命令的操作類(lèi)型在服務(wù)器端需用科平,在客戶(hù)端解析服務(wù)器反饋的數(shù)據(jù)時(shí)也需用。)淤击;
每增加一個(gè)網(wǎng)絡(luò)命令匠抗,就增加一個(gè)C++類(lèi)故源,這些命令類(lèi)有共同的基類(lèi)污抬;
在命令基類(lèi)里聲明了接口函數(shù)(C++中為純虛函數(shù))供每個(gè)命令子類(lèi)獨(dú)立實(shí)現(xiàn);
-
當(dāng)使用命令時(shí)绳军,只需要構(gòu)造出命令子類(lèi)(等號(hào)左邊為基類(lèi)指針變量印机,等號(hào)右邊通過(guò)new關(guān)鍵字創(chuàng)建命令子類(lèi)),設(shè)置好命令執(zhí)行需要的相關(guān)參數(shù)门驾,最后調(diào)用命令執(zhí)行函數(shù)exec()即可向服務(wù)器發(fā)送數(shù)據(jù)射赛。
命令基類(lèi)
commandabstract .h
#ifndef COMMANDABSTRACT_H
#define COMMANDABSTRACT_H
#include <QObject>
#include <QVariantHash>
class QTcpSocket;
class CommandAbstract : public QObject
{
Q_OBJECT
public:
explicit CommandAbstract(QObject *parent = 0);
//接口:準(zhǔn)備要發(fā)送給服務(wù)器的數(shù)據(jù)
//參數(shù):cmdArgs用于傳遞要發(fā)送給服務(wù)器的數(shù)據(jù)(或叫做命令參數(shù))
//說(shuō)明:在命令子類(lèi)中實(shí)現(xiàn)該接口,并將命令參數(shù)保存到m_sendingData成員變量中
//(m_sendingData為最終發(fā)送給服務(wù)器的數(shù)據(jù))
virtual void prepareSendingData(const QVariantHash& cmdArgs) = 0;
//接口:解析來(lái)自服務(wù)器的響應(yīng)數(shù)據(jù)奶是,保存到m_parsedResponseData成員變量中
//參數(shù):data為從服務(wù)器讀取到的響應(yīng)數(shù)據(jù)
//說(shuō)明:在命令子類(lèi)中實(shí)現(xiàn)該接口楣责,每個(gè)網(wǎng)絡(luò)命令都有不同的解析方式竣灌,因此這里抽象為接口
virtual void parseResponseData(const QByteArray& data) = 0;
//獲取要發(fā)送給服務(wù)器的數(shù)據(jù)m_sendingData
virtual QByteArray getSendingData() const;
//設(shè)置要發(fā)送給服務(wù)器的數(shù)據(jù)m_sendingData
virtual void setSendingData(const QByteArray& data);
//執(zhí)行命令,即:向服務(wù)器發(fā)送數(shù)據(jù)m_sendingData
void exec();
//設(shè)置QTcpSocket指針到命令類(lèi)中秆麸,以實(shí)現(xiàn)網(wǎng)絡(luò)通信功能
void setTcpSocket(QTcpSocket* tcpSocket);
//設(shè)置命令序列初嘹,記錄命令編號(hào),根據(jù)需要設(shè)置
void setCmdIndex(int index);
//設(shè)置命令操作類(lèi)型(當(dāng)相同的命令子類(lèi)需要實(shí)現(xiàn)不同的功能時(shí)使用沮趣,如:當(dāng)要使用相同的
//命令子類(lèi)發(fā)送不同的數(shù)據(jù)到服務(wù)器時(shí)屯烦,通過(guò)操作類(lèi)型來(lái)區(qū)分服務(wù)器響應(yīng)的數(shù)據(jù))
void setOperType(int operType);
protected:
int getCmdIndex() const;
//獲取命令操作類(lèi)型
int getCmdOperType() const;
signals:
//通過(guò)信號(hào)對(duì)外通知命令執(zhí)行數(shù)據(jù)m_parsedResponseData
void infoResultData(QVariantHash& parsedResponseData,QString& errorString);
public slots:
//當(dāng)接收到網(wǎng)絡(luò)數(shù)據(jù)響應(yīng)時(shí)執(zhí)行該槽函數(shù)
void onReadyRead();
private:
QVariantHash m_cmdData;//命令參數(shù)
QByteArray m_sendingData;//最終發(fā)送給服務(wù)器的命令數(shù)據(jù)
QVariantHash m_parsedResponseData;//解析之后的服務(wù)器響應(yīng)數(shù)據(jù)
int m_cmdIndex;
int m_cmdOperType;//命令操作類(lèi)型
QTcpSocket *m_tcpSocket;
};
#endif // COMMANDABSTRACT_H
commandabstract .cpp
#include "commandabstract.h"
#include <QTcpSocket>
CommandAbstract::CommandAbstract(QObject *parent) :
QObject(parent),
m_cmdIndex(0),
m_cmdOperType(0),
m_tcpSocket(NULL)
{
}
QByteArray CommandAbstract::getSendingData() const
{
return m_sendingData;
}
void CommandAbstract::setSendingData(const QByteArray &data)
{
m_sendingData = data;
}
void CommandAbstract::exec()
{
if(m_tcpSocket == NULL)
return;
m_tcpSocket->write(m_sendingData);
}
void CommandAbstract::setTcpSocket(QTcpSocket *tcpSocket)
{
m_tcpSocket = tcpSocket;
connect(m_tcpSocket,SIGNAL(readyRead()),this,SLOT(onReadyRead()));
}
void CommandAbstract::setCmdIndex(int index)
{
m_cmdIndex = index;
}
void CommandAbstract::setOperType(int operType)
{
m_cmdOperType = operType;
}
int CommandAbstract::getCmdIndex() const
{
return m_cmdIndex;
}
int CommandAbstract::getCmdOperType() const
{
return m_cmdOperType;
}
void CommandAbstract::onReadyRead()
{
QByteArray readData = m_tcpSocket->readAll();
parseResponseData(readData);
}
命令子類(lèi)
這里DemoCommand為命令子類(lèi),實(shí)際中可以根據(jù)需要派生出許多這樣的子類(lèi)向服務(wù)器發(fā)送不同的命令請(qǐng)求房铭。DemoCommand類(lèi)實(shí)現(xiàn)了基類(lèi)中的兩個(gè)接口函數(shù)驻龟。本例中,DemoCommand命令把數(shù)據(jù)key1和key2發(fā)送給服務(wù)程序缸匪,服務(wù)程序又將數(shù)據(jù)原樣返回到客戶(hù)端翁狐。
democommand.h
#ifndef DEMOCOMMAND_H
#define DEMOCOMMAND_H
#include <QObject>
#include "commandabstract.h"
class DemoCommand : public CommandAbstract
{
Q_OBJECT
public:
explicit DemoCommand(QObject *parent = 0);
virtual void prepareSendingData(const QVariantHash& cmdArgs);
virtual void parseResponseData(const QByteArray& data);
};
#endif // DEMOCOMMAND_H
democommand.cpp
#include "democommand.h"
#include <QDebug>
#include <QJsonObject>
#include <QJsonDocument>
#include <QVariantHash>
DemoCommand::DemoCommand(QObject *parent):CommandAbstract(parent)
{
}
//把發(fā)送給服務(wù)器的數(shù)據(jù)key1和key2以json格式設(shè)置到命令基類(lèi)中,共命令執(zhí)行函數(shù)exec()使用
void DemoCommand::prepareSendingData(const QVariantHash &cmdArgs)
{
QString data1 = cmdArgs.value("key1").toString();
QString data2 = cmdArgs.value("key2").toString();
QJsonObject jsonObj;
jsonObj.insert("key1",data1);
jsonObj.insert("key2",data2);
QJsonDocument jsonDoc(jsonObj);
this->setSendingData(jsonDoc.toJson());
qDebug()<<"m_sendingData = "<<this->getSendingData();
}
//解析來(lái)自服務(wù)器的反饋數(shù)據(jù)凌蔬,并通過(guò)信號(hào)發(fā)送到界面
void DemoCommand::parseResponseData(const QByteArray &data)
{
//根據(jù)不同的操作類(lèi)型谴蔑,對(duì)服務(wù)器返回?cái)?shù)據(jù)data進(jìn)行解析
qDebug()<<__LINE__<<__FUNCTION__<<"this->getCmdOperType() = "<<this->getCmdOperType();
qDebug()<<__LINE__<<__FUNCTION__<<"Read data = "<<data;
QVariantHash response;
response.insert("response",data);
emit this->infoResultData(response,QString());
switch (this->getCmdOperType()) {
case 1:{
}break;
case 2:{
}break;
case 3:{
}break;
default:
break;
}
this->deleteLater();
}
使用方法
void MainWindow::on_pushButtonSendCmd_clicked()
{
if(!m_tcpSocked->isOpen()){
m_tcpSocked->connectToHost("127.0.0.1",9090);
m_tcpSocked->waitForConnected();
}
QVariantHash cmdArgs;
cmdArgs.insert("key1","data1");
cmdArgs.insert("key2","data2");//data1和data2是模擬要發(fā)送給服務(wù)器的命令數(shù)據(jù)
CommandAbstract* cmd = new DemoCommand();
connect(cmd,SIGNAL(infoResultData(QVariantHash&,QString&)),this,SLOT(onInfoResultData(QVariantHash&,QString&)));
cmd->setCmdIndex(1);
cmd->setOperType(1);
cmd->prepareSendingData(cmdArgs);
cmd->setTcpSocket(m_tcpSocked);
cmd->exec();
}
//接收來(lái)自服務(wù)器反饋的數(shù)據(jù)
void MainWindow::onInfoResultData(QVariantHash &parsedResponseData, QString &errorString)
{
QString dataFromServer = parsedResponseData.value("response").toString();
ui->textEdit->append(dataFromServer);
}
運(yùn)行效果
源代碼下載
百度網(wǎng)盤(pán)分享地址:
鏈接:https://pan.baidu.com/s/1O36ltEmhY_ptAlTu-MpJ4w
提取碼:et2w
為了方便示例程序的運(yùn)行,專(zhuān)門(mén)附帶了服務(wù)器端的命令行可執(zhí)行程序commandmodeserver.exe龟梦,本例中的客戶(hù)端程序編譯運(yùn)行之后隐锭,會(huì)與它連接,它會(huì)反饋客戶(hù)端發(fā)送的數(shù)據(jù)计贰。