目錄
前言
1. 先明確大綱
2. 基于時序圖開始UML類圖的設計
3. 基于UML類圖的核心偽代碼段
3.1 訪問網路模塊背景關系類
3.2 依據后臺介面創建model/entity類 此處為 samplecode
3.3 NetWorkTask類物件樹:
3.4 決議器物件樹:
3.5 Provider 類
extension 3.5
4. 結語
前言
現代應用(app)對于網路的依賴可以說無處不在,網路模塊越來越重要, 連基本簡單的Demo都離不開網路的應用,
所以網路庫設計的好壞直接影響到系統的健壯性以及開發效率,本文基于Qt 網路模塊來跟大家一起來簡單探討和學習怎樣利用OOC思想設計一個簡單易用的業務網路庫,
1. 先明確大綱
我想既然要開發一個業務網路庫那么先要想到一個網路庫的核心作業流程是什么, 如下圖,一個常規網路庫需要執行的主要流程,
個人建議先抽象出一個作業流程圖,在基于圖中的流程來抽象網路庫的設計,

可以發現如果基于上圖設計網路庫時會有如下問題:
1. 客戶訪問網路介面是線性的,無法做到延遲呼叫,對于復雜業務彈性不足,代碼過于程序式,
2. 服務端response資料放在Provider介面中會造成該類代碼膨脹以及難以維護,
3. 對于回應結果的通知依賴委托、回呼、信號都違背了最小依賴原則,
那么,該怎樣解決以上問題呢? 核心思想還是抽象, 增加網路任務與決議物件樹后呼叫結構

1. 抽象出介面請求網路任務,回傳任務給到客戶代碼, 由客戶代碼決定何時執行任務,
2. 客戶代碼只關心任務,由任務給出回應信號, 而不需要去實作大量委托介面、注冊各種回呼, 代碼更加簡潔、低耦合、方便擴展也更加符合最小依賴原則,
3. Provider中的決議任務交由parseObjectTree去動態決議,Provider更多的去做屬于自己的事情, 也解決了該結構中大量的決議資料代碼,
2. 基于時序圖開始UML類圖的設計
有了時序圖后我相信這時候作業已經完成一大半了, 根據主要基線設計出關鍵類即可,
Provider類設計:
該類為直接對應客戶介面,需提供所有指定網路介面,
NetWorkTask類設計:
異步請求任務類,封裝執行介面與提供請求資料Model-Entity結果,
ParseObjectTree類設計:
應用后臺對應的資料物體類(model -Entity)parser器,用于決議后臺資料(json/xml)資料對應,
訪問網路模塊背景關系類設計:
該類主要職責為管理Qt網路模塊中QNetworkAccessManaget實體以及發起網路呼叫,
Model/Entity類-對應后臺回應資料
回應json/xml 映射model類,
依據上述描述最終的UML類圖設計如下圖:

3. 基于UML類圖的核心偽代碼段
有了第2節的UML類圖后, 剩下的就是編碼了,到這兒基本完成就完成百分七十的內容(😀 ),下面給出主線偽代碼,希望能幫助到有需要的人(😬 ),
3.1 訪問網路模塊背景關系類
class NetworkAccessContext
{
public:
virtual ~NetworkAccessContext() {}
virtual QNetworkReply* get(const QNetworkRequest& request) = 0;
virtual QNetworkReply* post(const QNetworkRequest& request, const QByteArray& data) = 0;
virtual QNetworkReply* post(const QNetworkRequest& request, const QIODevice* ioData) = 0;
virtual QNetworkReply* deleteResource(const QNetworkRequest& request) = 0;
virtual QNetworkReply* put(const QNetworkRequest& request, const QByteArray& data) = 0;
virtual QNetworkReply* put(const QNetworkRequest& request, const QIODevice* ioData) = 0;
virtual void setNetworkAccessManager(const QNetworkAccessManager*) {}
virtual QNetworkAccessManager* getNam() = 0;
};
class DefaultNetworkAccessContext : public NetworkAccessContext
{
public:
DefaultNetworkAccessContext() {
initNetworkAccessManager();
}
virtual ~DefaultNetworkAccessContext() override {
// todo delete accessmanager
}
virtual QNetworkReply* get(const QNetworkRequest& request) override {
return getNam()->get(request);
}
virtual QNetworkReply* post(const QNetworkRequest& request, const QByteArray& data) override {
return getNam()->post(request, data);
}
virtual QNetworkReply* post(const QNetworkRequest& request, const QIODevice* ioData) override {
return getNam()->post(request, const_cast<QIODevice*>(ioData));
}
virtual QNetworkReply* deleteResource(const QNetworkRequest& request) override {
return getNam()->deleteResource(request);
}
virtual QNetworkReply* put(const QNetworkRequest& request, const QByteArray& data) override {
return getNam()->put(request, data);
}
virtual QNetworkReply* put(const QNetworkRequest& request, const QIODevice* ioData) override {
return getNam()->put(request, const_cast<QIODevice*>(ioData));
}
virtual void setNetworkAccessManager(const QNetworkAccessManager* nam) override {
const auto thread = QThread::currentThread();
if (m_thread2Nam.contains(thread)) {
m_thread2Nam[thread]->deleteLater();
}
m_thread2Nam[thread] = const_cast<QNetworkAccessManager*>(nam);
}
virtual QNetworkAccessManager* getNam() override {
if (!m_thread2Nam.contains(QThread::currentThread())) {
initNetworkAccessManager();
}
return m_thread2Nam[QThread::currentThread()];
}
private:
void initNetworkAccessManager() {
if (!m_thread2Nam.contains(QThread::currentThread())) {
m_thread2Nam[QThread::currentThread()] = new QNetworkAccessManager();
}
}
private:
QHash<QThread*, QNetworkAccessManager*> m_thread2Nam;
};
3.2 依據后臺介面創建model/entity類 此處為 samplecode
// samplecode server response common field entity
struct ResponseBaseEntity {
uint code;
QString message;
// more fields ...
};
// entity sampleCode
class ResponseEntity1 {
public:
typedef QList<ResponseEntity1> List;
class DataParser;
int getField1() { return m_field1; }
void setField1(int value) {m_field1 = value;}
QString getField2() {return m_field2;}
void setField2(const QString& value) {m_field2 = value;}
// todo more field setter and getter
// ...
private:
int m_field1;
QString m_field2;
// todo more fields ...
};
class ResponseEntity2 {
public:
typedef QList<ResponseEntity2> List;
class DataParser;
int getField1() { return m_field1; }
void setField1(int value) {m_field1 = value;}
QString getField2() {return m_field2;}
void setField2(const QString& value) {m_field2 = value;}
// todo more field setter and getter
// ...
private:
int m_field1;
QString m_field2;
// todo more fields ...
};
3.3 NetWorkTask類物件樹:
// NetWorkTask
class NetWorkTask : public QObject {
Q_OBJECT
public:
void start() { }
void abort() {
if (m_reply) {
m_reply->abort();
m_reply->deleteLater();
}
deleteLater();
}
ResponseBaseEntity getResponseBaseEntity() {
return m_respBaseEntity;
}
signals:
void taskFinish();
protected:
NetWorkTask(NetworkAccessContext* nac) : m_acContext(nac) {}
virtual QNetworkReply* executeRequest() = 0;
virtual void parse(const QByteArray& data) = 0;
void setResponseEntity(const ResponseBaseEntity& entity) {
m_respBaseEntity = entity;
}
private:
void work() {
if (m_reply) {return;}
m_reply = executeRequest();
if (m_reply) {
connect(m_reply, &QNetworkReply::finished, this , [this]() {
if (m_reply->error() == QNetworkReply::NoError) {
const auto data = m_reply->readAll();
parse(data);
} else {
m_respBaseEntity.code = m_reply->error();
m_respBaseEntity.message = m_reply->errorString();
}
});
}
emit taskFinish();
deleteLater();
}
protected:
NetworkAccessContext* m_acContext{nullptr};
private:
QNetworkReply* m_reply {nullptr};
ResponseBaseEntity m_respBaseEntity;
};
// PostTask
class PostTask : public NetWorkTask
{
public:
PostTask(NetworkAccessContext* nac, const QNetworkRequest& request, const QByteArray& data) :
NetWorkTask(nac), m_postData(data), m_request(std::move(request)) {}
PostTask(NetworkAccessContext* nac, const QNetworkRequest& request, QIODevice* data): NetWorkTask(nac), m_request(request) {
m_postData = data->readAll();
}
virtual QNetworkReply* excuteRequest() {
return m_acContext->post(m_request, m_postData);
}
virtual void parse(const QByteArray& data) {
Q_UNUSED(data)
// todo parse post request -> server response data
}
private:
QByteArray m_postData;
QNetworkRequest m_request;
};
// GetTask
class GetTask : public NetWorkTask {
public:
GetTask(NetworkAccessContext* nac, const QNetworkRequest& request) :
NetWorkTask (nac), m_request(std::move(request)) {}
virtual QNetworkReply* executeRequest() override {
return m_acContext->get(m_request);
}
private:
QNetworkRequest m_request;
};
// ItemGetTask
template<typename T>
class ItemGetTask : public GetTask
{
public:
ItemGetTask(NetworkAccessContext* nac, const QNetworkRequest& request): GetTask(nac, request) {}
virtual void parse(const QByteArray& data) override {
Q_UNUSED(data)
typename T::DataParser parser;
m_responseEntity = parser.parseObject(data);
}
T getResult() {
return m_responseEntity;
}
private:
T m_responseEntity;
};
// ListItemGetTask
template<class T>
class ListItemGetTask : public GetTask {
public:
ListItemGetTask(NetworkAccessContext* nac, const QNetworkRequest& request) :
GetTask(nac, request) {}
typename T::List getEntity() {return m_getListObject;}
virtual void parse(const QByteArray& data) override {
//
typename T::List out;
typename T::DataParser parser;
m_getListObject = parser.parseObjectList(data);
}
private:
typename T::List m_getListObject;
};
// PutTask
class PutTask : public NetWorkTask {
// Roughly the same as postTask class
// todo Based on service response parse custom data
virtual void parse(const QByteArray& data) {
Q_UNUSED(data)
// todo parse your needs dataField
}
};
// DelResourceTask
class DelResourceTask : public NetWorkTask {
public:
// Roughly the same as postTask class
// todo Based on service response parse custom data
};
3.4 決議器物件樹:
template<class T>
class Parser {
public:
Parser() {}
virtual ~Parser() {}
T parseObject(const QByteArray& data) {
// todo parse base entity
// parse T
return parse(data);
}
typename T::List parseObjectList() {
typename T::List out;
// todo elements
return out;
}
ResponseBaseEntity responseBaseEntity() {return m_baseEntity;}
protected:
virtual T parse(const QByteArray& data) = 0;
private:
ResponseBaseEntity m_baseEntity;
};
class ResponseEntity1::DataParser : public Parser<ResponseEntity1>
{
public:
virtual ~DataParser() override {}
private:
virtual ResponseEntity1 parse(const QByteArray& data) override {
Q_UNUSED(data)
// todo parse entity
return ResponseEntity1();
}
};
class ResponseEntity2::DataParser : public Parser<ResponseEntity2>
{
public:
private:
virtual ResponseEntity2 parse(const QByteArray& data) override {
Q_UNUSED(data)
// todo parse entity
return ResponseEntity2();
}
};
3.5 Provider 類
class Provider {
public:
Provider() {}
PostTask postObjectData(const QByteArray& data) {
// todo make QNetworkRequest
return PostTask(m_nac, QNetworkRequest(), data);
}
ItemGetTask<ResponseEntity1> getItemEntity(paramters ...) {
// todo make QNetworkRequest
return new ItemGetTask<ResponseEntity1>(m_nac, QNetworkRequest());
}
ListItemGetTask<ResponseEntity2> getListEntity(paramters ...) {
// todo make QNetworkRequest
return new ItemGetTask<ResponseEntity1>(m_nac, QNetworkRequest());
}
// todo provider put、delete interface ...
private:
QString m_baseUrl;
NetworkAccessContext* m_nac;
};
extension 3.5:
Provider介面/類是直接面向客戶代碼的,為了滿足簡單易用的原則建議可以對Provider提供管理類, 這樣做主要有以下幾點原因:
1. 對于客戶代碼更加友好, 無需實體化Provider, 根據Provider管理介面類來獲取所需要的Provider實體,更加簡單易用,
2. 由庫管理Provider物件的生命周期,防止外部代碼初始化多余Provider類,
3. 方便擴展,如果后臺restful API升級可以提供多個Provider, 由客戶代碼自由選擇后臺版本介面,把復雜留給自己內部庫,把簡單留給外部代碼,
4. 結語
全文只是簡單的探討了怎樣利用OOC思想來做一個簡單的業務網路庫, 想做的也只是一個拋磚引玉的作用, 當然文中講述的網路庫還缺少很多機制, 比如說攔截、重試、各種配置等機制, 但這些后期都可以在此基礎繼續完善,
如果本文對你有用或者有所啟發, 歡迎轉發,收藏、留言交流, 哈哈 (😄 ) , 謝謝你的閱讀, 文中有所不當之處還望指正,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/264242.html
標籤:其他
上一篇:抖音—黑馬選手
