使用代理模式來增強類的功能:ToastProxy和DesktopToast
Documentation: v1.0.0 Specified for Version v1.12.0,First Release in 2025/7/12
Documenation belongs to Projects: Charliechen114514/CCIMXDesktop: This is a Simple Desktop with Common Utilities for Embedded Device System using Qt6
前言
? 這個算設計模式的部分,筆者當時正在設計的是如何讓我的Desktop支持擴展的從Protocol Json Buffer轉換到我的Toast可以識別的Meta Info的問題。這也是筆者寫下這篇設計文檔的原因——設計文檔鴿了一個月(之前一直認為這個項目還沒有成型的框架,因此遲遲沒有動筆)
關于Proxy設計模式
? 筆者很早就學習過設計模式,至少接觸過一部分,Proxy,英語好的朋友知道,這是代理的意思。代理大伙都知道代理人,替本人出任做工作的代理人。這里我們說的代理,就是提供一個新的類代理舊的類,當然,如果是完全做一樣的事情,我們沒必要請出Proxy,對吧。那么,Proxy顯然要比原先的類多做一部分工作,在筆者看來,Proxy設計模式是對原對象一次無痛(什么是無痛的?指的是不會改動原有類的任何功能,通過和其他類的協作達到增強原有類可以做的事情)擴展。
? 現在說說筆者遇到的問題,看過筆者DesktopToast源碼的朋友就知道,DesktopToast是一個簡單的通知欄滑動類,實際上,我們最關心也是他最本職的功能是這個:
class DesktopToast : public QWidget {Q_OBJECT
public:/*** @brief Construct a new Desktop Toast object* * @param parent */explicit DesktopToast(QWidget* parent = nullptr);/*** @brief set_message push the toast display into the* toast display sessions* @param message* @note this will make a queue like display.* so, if the toast is currently animating, it will not display the new* messages immediately, but wait until the current message is finished*/void set_message(const QString& message);... // 下面的工作是私有成員,set message就是告訴我們的 toast顯示什么東西,很簡單吧!
? 非常好,但是現在呢,筆者正在抽象一個服務類,簡單的講,就是監聽一個特定的文件夾下的json文件解析滿足ToastMetaProtocol要求格式的json文件,一旦監聽到新的文件,我們就做解析轉換,發送信號交給一個類處理并且做顯示,由于我們使用的是Qt,他是按照信號的方式進行傳遞的,筆者經過權衡考慮,認為設計一個類來作為代表,比堆一大堆函數的串行調用要靠譜(真不敢想后者的可擴展性。。。)
? 經過筆者的一番折騰,現在我們的Server類非常的干凈
/*** @brief The ToastPostServer class* This is a server of toast infomation level*/
class ToastPostServer : public DesktopServerBase {Q_OBJECT
public:ToastPostServer(DesktopToast* toast, const QString& monitoring_path, QObject* parent);private:// 這里就是放Toast嘛?顯然不行!ToastReceiver* receiver;
};
? 其中Receiver類也很簡單,他將監聽到的新的Json file轉化成我規定的ToastMetaProtocol對象,在一次信號發射中攜帶ToastMetaProtocol對象走出去,但是現在問題來了,我們的DesktopToast本身不支持ToastMetaProtocol,而且他是特定于我們桌面系統的一個類,如果我強行修改了DesktopToast類,我之后就沒法在其他地方復用了,這咋辦呢?我也不想繼承,因為我不想對其他冗余的接口占用(雖然也是辦法,但是實際上后面我還會增強關于Toast本身的調用,我還要修改一大堆代碼防止客戶調用父類的Partial的功能亂改一通)。歸根揭底,是因為我們在這里是要求DesktopToast類按照一定的順序和其他相關的類進行協作,本質上不算DesktopProxy對象的工作,我們不能強行算在DesktopToast頭上。
? 這就是下面這個類的功能了:
#ifndef TOASTPROXY_H
#define TOASTPROXY_H
#include "core/server/toast_file_gen_and_receiver/ToastMetaProtocol.h"
class DesktopToast;
class ToastProxy : public QObject {
public:ToastProxy() = delete;explicit ToastProxy(DesktopToast* toast, QObject* parent);
public slots:void process_toastMeta(const ToastMetaProtocol& protocolMeta);private:DesktopToast* toast;
};#endif // TOASTPROXY_H
? ToastProxy作為代理對象,增強(延展)了我們的DesktopToast的功能
- 接收上層組件(如 ToastReceiver)傳來的
ToastMetaProtocol
對象; - 解析并轉化為
DesktopToast
可處理的格式; - 向
DesktopToast
發起消息展示; - 為未來可能新增的中間處理(如日志、過濾、限流等)提供擴展點。
? 這里的一個具體的增強就體現在process_toastMeta,一方面我們的對象可以接受其他對象對我們DesktopToast的通知,而不是直接溝通增加強耦合,而是去隔一層ToastProxy的轉化功能發給DesktopToast可以聽懂的功能,這樣的話,任何的擴展的協作的功能,都可以直接在ToastProxy上加,絲毫不會影響兩側使用這個類做事情和被要求做的事情的類的任何一點行為。這就是代理模式在這里的一次使用。
? 現在有了這個類,我們的調用鏈看起來就像:
ToastPostServer|v
ToastReceiver --- (signal: protocolMetaReady) --> ToastProxy --- (call) --> DesktopToastv
ToastReceiver --- (signal: protocolMetaReady) --> ToastProxy --- (call) --> DesktopToast