文章目錄
- 一、代理模式的定義
- 二、實例分析
- 三、示例代碼
一、代理模式的定義
??代理模式是一種結構型設計模式,它為某個對象提供一個代理或占位符,以控制對這個對象的訪問。簡單來說代理對象在客戶端和目標對象之間起到中介作用,客戶端并不會直接操作目標對象,而是通過代理對象間接訪問。
代理模式常用于以下情況:
- 遠程代理:為遠程對象(在不同地址空間、服務器上)提供本地代理,隱藏遠程訪問的復雜性。
- 虛擬代理:在需要消耗大量資源時,先用代理延遲對象的創建或初始化。(如:大圖片的延遲加載)
- 保護代理:控制不同用戶對對象的訪問權限。(如:文件系統訪問控制)
- 智能代理:在訪問對象時附加額外操作(如緩存、日志、計數、線程安全控制)。
代理模式結構:
二、實例分析
問題:
為什么要控制對于某個對象的訪問呢? 舉個例子: 有這樣一個消耗大量系統資源的巨型對象, 你只是偶爾需要使用它, 并非總是需要。
你可以實現延遲初始化: 在實際有需要時再創建該對象。 對象的所有客戶端都要執行延遲初始代碼。 不幸的是, 這很可能會帶來很多重復代碼。
在理想情況下, 我們希望將代碼直接放入對象的類中, 但這并非總是能實現: 比如類可能是第三方封閉庫的一部分。
解決方案:
代理模式建議新建一個與原服務對象接口相同的代理類, 然后更新應用以將代理對象傳遞給所有原始對象客戶端。 代理類接收到客戶端請求后會創建實際的服務對象, 并將所有工作委派給它。
這有什么好處呢? 如果需要在類的主要業務邏輯前后執行一些工作, 你無需修改類就能完成這項工作。 由于代理實現的接口與原類相同, 因此你可將其傳遞給任何一個使用實際服務對象的客戶端。
真實世界類比:
信用卡是銀行賬戶的代理, 銀行賬戶則是一大捆現金的代理。 它們都實現了同樣的接口, 均可用于進行支付。 消費者會非常滿意, 因為不必隨身攜帶大量現金; 商店老板同樣會十分高興, 因為交易收入能以電子化的方式進入商店的銀行賬戶中, 無需擔心存款時出現現金丟失或被搶劫的情況。
代碼實現:
- 抽象接口:Payment,定義統一的 pay(amount) 方法。
- 真實主題(RealSubject):Cash,表示真正的支付方式(現金支付)
- 代理(Proxy):CreditCard,作為銀行賬戶的代理,內部持有一個 BankAccount(或 Cash)對象,通過代理實現支付。
#include <iostream>
#include <memory>
using namespace std;// 抽象接口
class Payment {
public:virtual void pay(double amount) = 0;virtual ~Payment() = default;
};// 真實主題:現金支付
class Cash : public Payment {
public:void pay(double amount) override {cout << "支付現金: " << amount << " 元" << endl;}
};// 銀行賬戶(被代理對象)
class BankAccount {
public:void withdraw(double amount) {cout << "從銀行賬戶扣款: " << amount << " 元" << endl;}
};// 代理:信用卡支付
class CreditCard : public Payment {
private:BankAccount* account; // 代理對象內部持有真實對象
public:CreditCard(BankAccount* acc) : account(acc) {}void pay(double amount) override {cout << "使用信用卡支付: " << amount << " 元" << endl;account->withdraw(amount); // 實際通過銀行賬戶扣款}
};// 客戶端
int main() {// 使用現金支付unique_ptr<Payment> cashPayment = make_unique<Cash>();cashPayment->pay(100);// 使用信用卡支付(代理)BankAccount bankAcc;unique_ptr<Payment> creditPayment = make_unique<CreditCard>(&bankAcc);creditPayment->pay(200);return 0;
}
三、示例代碼
示例一:
#include <iostream>
#include <memory>
using namespace std;// 抽象主題
class Subject {
public:virtual void request() = 0;virtual ~Subject() = default;
};// 真實主題
class RealSubject : public Subject {
public:void request() override {cout << "RealSubject: Handling request." << endl;}
};// 代理類
class Proxy : public Subject {
private:unique_ptr<RealSubject> realSubject;
public:void request() override {if (!realSubject) {cout << "Proxy: Creating RealSubject." << endl;realSubject = make_unique<RealSubject>();}cout << "Proxy: Delegating request to RealSubject." << endl;realSubject->request();}
};// 客戶端
int main() {Proxy proxy;proxy.request(); // 第一次訪問,創建真實對象proxy.request(); // 第二次訪問,直接使用已有對象return 0;
}
示例二:
數據庫模塊(QSqlQuery)+ 緩存代理的實際應用示例
設計思路:
- 接口類 IDatabase:定義通用的查詢接口
- 真實類 RealDatabase:直接使用 QSqlDatabase + QSqlQuery 訪問數據庫。
- 代理類 DatabaseProxy:在真實查詢前先檢查緩存,緩存命中就直接返回結果,避免重復訪問數據庫。
#include <QCoreApplication>
#include <QtSql/QSqlDatabase>
#include <QtSql/QSqlQuery>
#include <QtSql/QSqlError>
#include <QDebug>
#include <QVariant>
#include <QMap>
#include <QStringList>// 抽象接口
class IDatabase {
public:virtual QStringList query(const QString& sql) = 0;virtual ~IDatabase() = default;
};// 真實數據庫類
class RealDatabase : public IDatabase {
private:QSqlDatabase db;
public:RealDatabase() {// 連接 SQLite 數據庫db = QSqlDatabase::addDatabase("QSQLITE");db.setDatabaseName(":memory:"); // 內存數據庫,演示用if (!db.open()) {qWarning() << "Database error:" << db.lastError().text();}// 初始化數據QSqlQuery q;q.exec("CREATE TABLE users (id INT, name TEXT)");q.exec("INSERT INTO users VALUES (1, 'Alice')");q.exec("INSERT INTO users VALUES (2, 'Bob')");}QStringList query(const QString& sql) override {QSqlQuery q;QStringList result;if (!q.exec(sql)) {qWarning() << "SQL error:" << q.lastError().text();return result;}while (q.next()) {result << q.value(0).toString(); // 只取第一列}qDebug() << "[RealDatabase] Executing:" << sql;return result;}
};// 代理類,帶緩存
class DatabaseProxy : public IDatabase {
private:RealDatabase* realDb;QMap<QString, QStringList> cache; // SQL -> 查詢結果
public:DatabaseProxy() : realDb(nullptr) {}QStringList query(const QString& sql) override {// 緩存檢查if (cache.contains(sql)) {qDebug() << "[Proxy] Cache hit for:" << sql;return cache[sql];}// 創建真實對象if (!realDb) {realDb = new RealDatabase();}qDebug() << "[Proxy] Cache miss, querying DB:" << sql;QStringList result = realDb->query(sql);// 存入緩存cache[sql] = result;return result;}~DatabaseProxy() {delete realDb;}
};// 測試
int main(int argc, char *argv[]) {QCoreApplication a(argc, argv);IDatabase* db = new DatabaseProxy();qDebug() << db->query("SELECT name FROM users WHERE id=1");qDebug() << db->query("SELECT name FROM users WHERE id=2");qDebug() << db->query("SELECT name FROM users WHERE id=1"); // 命中緩存delete db;return 0;
}