设计模式 - 代理模式(Proxy Pattern)
代理模式(Proxy Pattern)是一种结构型设计模式,它为其他对象提供了一种代理,以控制对这个对象的访问。代理模式可以用于延迟对象的创建和初始化、控制对对象的访问、在访问对象时添加额外的功能等。
flyfish
代理模式的关键概念
-
代理(Proxy) :实现与目标对象相同的接口,用来控制对目标对象的访问。
-
目标对象(RealSubject) :定义了代理对象代表的真实对象。
-
客户端(Client) :通过代理对象间接访问目标对象。
应用场景
延迟初始化(虚拟代理)
如果你有一个需要很多资源但并不经常使用的对象,可以使用代理模式。这种情况下,代理可以在你第一次需要对象时才真正创建它,而不是在程序启动时就创建。这种方式可以节省系统资源,直到你确实需要使用这个对象。
访问控制(保护代理)
当你想限制只有某些客户端可以访问某个对象时,可以使用代理模式。例如,如果这个对象是操作系统的重要部分,而你只希望合法的程序访问它,代理可以在检查客户端的权限后再决定是否允许访问。
远程服务的本地执行(远程代理)
当你的服务对象在远程服务器上时,可以使用代理模式。在这种情况下,代理会处理所有与网络相关的细节,比如通过网络发送请求。这样客户端就可以像使用本地对象一样使用远程对象,而不用关心网络通信的复杂性。
记录日志(日志记录代理)
如果你需要记录对某个服务对象的请求历史,可以使用代理模式。代理可以在将请求传递给服务对象之前,先把请求信息记录下来。这对调试或审计很有帮助。
缓存请求结果(缓存代理)
当请求的结果非常大或者生成这些结果需要花费很多时间时,可以使用代理模式来缓存这些结果。代理可以保存已经请求过的结果,以便在再次请求相同结果时直接返回缓存,而不是重新计算。
智能引用
当一个耗费大量资源的对象不再被使用时,你可能想要立即销毁它以释放系统资源。代理可以记录哪些客户端正在使用这个对象,并在没有客户端使用时销毁它。此外,如果对象没有被修改,代理还可以让其他客户端复用它,从而避免不必要的重复创建。
这些代理模式的应用场景让你的系统更高效、更安全,同时也使得某些功能的实现变得更加简洁。
一个简单的例子
-
Subject 类 :定义了抽象接口,
RealSubject
和Proxy
都实现了这个接口。 -
RealSubject 类 :目标对象,包含实际的请求处理逻辑。
-
Proxy 类 :代理对象,控制对目标对象的访问,可以在访问前后添加额外的功能,如权限检查和日志记录。
-
ClientCode 函数 :客户端代码,通过代理访问目标对象。
-
main 函数 :展示了如何使用代理模式通过代理访问目标对象。
#include <iostream>
#include <memory>
// 抽象接口类,定义真实对象和代理对象的公共接口
class Subject {
public:
virtual void Request() const = 0; // 抽象方法,具体操作由子类实现
virtual ~Subject() = default; // 虚析构函数,确保正确析构子类
};
// 目标对象类,定义了代理所代表的真实对象
class RealSubject : public Subject {
public:
void Request() const override {
std::cout << "RealSubject: Handling request." << std::endl; // 处理请求
}
};
// 代理类,实现与目标对象相同的接口
class Proxy : public Subject {
private:
std::unique_ptr<RealSubject> real_subject_; // 目标对象指针
// 检查访问权限的私有方法
bool CheckAccess() const {
std::cout << "Proxy: Checking access before firing a real request." << std::endl;
// 在这里可以进行权限检查,返回 true 表示有权限
return true;
}
// 记录日志的私有方法
void LogAccess() const {
std::cout << "Proxy: Logging the time of request." << std::endl;
// 在这里可以记录访问日志
}
public:
// 构造函数,初始化目标对象
Proxy() : real_subject_(std::make_unique<RealSubject>()) {}
void Request() const override {
// 先检查访问权限
if (CheckAccess()) {
// 调用真实对象的请求处理方法
real_subject_->Request();
// 记录日志
LogAccess();
}
}
};
// 客户端代码,通过代理访问真实对象
void ClientCode(const Subject& subject) {
subject.Request(); // 通过代理调用请求
}
int main() {
std::cout << "Client: Executing client code with a real subject:" << std::endl;
RealSubject real_subject; // 创建真实对象
ClientCode(real_subject); // 直接使用真实对象
std::cout << std::endl;
std::cout << "Client: Executing the same client code with a proxy:" << std::endl;
Proxy proxy; // 创建代理对象
ClientCode(proxy); // 使用代理对象
return 0;
}
模拟了一个第三方视频服务的远程接口,并通过代理类缓存请求结果以节省网络带宽
-
ThirdPartyTVLib 接口 :定义了视频服务的基本功能,包括列出视频、获取视频信息和下载视频。
-
ThirdPartyTVClass 类 :实现了
ThirdPartyTVLib
接口,模拟与远程视频服务的连接。 -
CachedTVClass 类 :实现了
ThirdPartyTVLib
接口,并在代理对象中实现了缓存功能,减少重复请求。 -
TVManager 类 :使用服务接口来渲染视频列表和视频页面,通过代理对象间接访问服务对象。
-
Application 类 :初始化服务对象、代理对象和管理器,并模拟用户交互。
#include <iostream>
#include <unordered_map>
#include <vector>
#include <string>
#include <memory>
// 远程服务接口,定义了视频服务的基本功能
class ThirdPartyTVLib {
public:
virtual std::vector<std::string> listVideos() = 0; // 列出视频
virtual std::string getVideoInfo(int id) = 0; // 获取视频信息
virtual void downloadVideo(int id) = 0; // 下载视频
virtual ~ThirdPartyTVLib() = default; // 虚析构函数
};
// 服务连接器的具体实现,模拟与腾讯视频的连接
class ThirdPartyTVClass : public ThirdPartyTVLib {
public:
std::vector<std::string> listVideos() override {
std::cout << "Fetching list of videos from Tencent Video." << std::endl;
// 模拟 API 请求
return {"Video1", "Video2", "Video3"};
}
std::string getVideoInfo(int id) override {
std::cout << "Getting video info for video ID " << id << " from Tencent Video." << std::endl;
// 模拟获取视频元数据
return "Video Info for ID " + std::to_string(id);
}
void downloadVideo(int id) override {
std::cout << "Downloading video ID " << id << " from Tencent Video." << std::endl;
// 模拟下载视频
}
};
// 缓存代理类,缓存视频请求结果以减少网络请求
class CachedTVClass : public ThirdPartyTVLib {
private:
std::unique_ptr<ThirdPartyTVLib> service_; // 实际服务对象
std::vector<std::string> listCache_; // 视频列表缓存
std::unordered_map<int, std::string> videoCache_; // 视频信息缓存
bool needReset_ = true; // 是否需要重置缓存
public:
// 构造函数,初始化实际服务对象
explicit CachedTVClass(std::unique_ptr<ThirdPartyTVLib> service) : service_(std::move(service)) {}
std::vector<std::string> listVideos() override {
if (listCache_.empty() || needReset_) {
listCache_ = service_->listVideos();
needReset_ = false; // 获取后重置标志
}
return listCache_;
}
std::string getVideoInfo(int id) override {
if (videoCache_.find(id) == videoCache_.end() || needReset_) {
videoCache_[id] = service_->getVideoInfo(id);
needReset_ = false;
}
return videoCache_[id];
}
void downloadVideo(int id) override {
if (!downloadExists(id) || needReset_) {
service_->downloadVideo(id);
needReset_ = false;
}
}
private:
// 私有辅助方法,检查下载是否存在(模拟功能)
bool downloadExists(int id) {
// 假设每次都需要下载(这里可实现实际检查逻辑)
return false;
}
};
// GUI 管理类,负责渲染视频信息
class TVManager {
protected:
ThirdPartyTVLib* service_; // 使用原始指针指向服务接口
public:
// 构造函数,初始化服务接口
explicit TVManager(ThirdPartyTVLib* service) : service_(service) {}
void renderVideoPage(int id) {
std::string info = service_->getVideoInfo(id);
std::cout << "Rendering video page for video ID " << id << ": " << info << std::endl;
}
void renderListPanel() {
std::vector<std::string> list = service_->listVideos();
std::cout << "Rendering video list panel: ";
for (const auto& video : list) {
std::cout << video << " ";
}
std::cout << std::endl;
}
void reactOnUserInput() {
renderListPanel(); // 模拟用户输入反应
renderVideoPage(1); // 模拟用户选择视频 ID 为 1
}
};
// 应用程序类,初始化代理和管理器
class Application {
public:
void init() {
std::unique_ptr<ThirdPartyTVLib> aTVService = std::make_unique<ThirdPartyTVClass>();
std::unique_ptr<ThirdPartyTVLib> aTVProxy = std::make_unique<CachedTVClass>(std::move(aTVService));
TVManager manager(aTVProxy.get());
manager.reactOnUserInput();
}
};
int main() {
Application app;
app.init(); // 初始化应用程序
return 0;
}