代理模式及优化 - 实践
代理模式(Proxy Pattern)是一种结构型设计模式,它允许通过创建一个代理对象来控制对另一个对象(称为真实对象或被代理对象)的访问。代理对象充当客户端和真实对象之间的中介,客户端通过代理对象间接访问真实对象,从而可以在不改变真实对象的前提下添加额外的功能或控制逻辑。
一、介绍
核心概念
- 主题接口(Subject Interface)
- 定义真实对象和代理对象的共同接口,确保客户端可以统一地使用它们。
- 客户端通过此接口与代理和真实对象交互。
- 真实主题(Real Subject)
- 实现主题接口的具体对象,提供实际的业务功能。
- 通常是开销较大或需要特殊处理的对象。
- 代理(Proxy)
- 实现与真实主题相同的接口,持有对真实主题的引用。
- 控制对真实主题的访问,并可以在调用前后添加额外逻辑。
优点
- 解耦客户端与真实对象
- 客户端无需知道真实对象的具体实现,降低耦合度
- 增强安全性
- 通过代理控制访问权限,保护敏感资源
- 提高性能
- 延迟加载和缓存机制优化资源利用
- 简化复杂系统
- 隐藏网络通信、资源管理等复杂细节
- 符合开闭原则
- 可以在不修改真实对象的情况下添加新的代理功能
缺点
- 增加系统复杂度
- 多层代理或复杂代理逻辑可能导致代码难以理解
- 性能开销
- 代理调用会引入额外的处理步骤,可能影响性能
- 可能导致设计过度
- 如果不需要额外控制或功能,使用代理会显得冗余
代理模式的使用时机
- 延迟初始化(虚拟代理)
- 当创建开销大的对象时,通过代理延迟到真正需要时再创建
- 如示例中的图片加载,首次访问时才加载资源
- 访问控制(保护代理)
- 限制对真实对象的访问权限
- 例如:验证用户权限后才允许访问敏感资源
- 远程对象代理(远程代理)
- 为位于不同地址空间的对象提供本地接口
- 如分布式系统中,代理负责网络通信细节
- 操作监控(智能引用代理)
- 在访问对象时附加额外操作
- 例如:引用计数、缓存、日志记录等
- 资源优化(缓存代理)
- 缓存频繁访问的对象结果
- 如数据库查询结果的临时缓存
二、实现
#include <iostream>
#include <string>
// 主题接口
class Image
{
public:
virtual void display() const = 0;
virtual ~Image() = default;
};
// 真实主题
class RealImage
: public Image {
private:
std::string filename;
public:
explicit RealImage(const std::string& filename) : filename(filename) {
loadFromDisk();
}
void display() const override {
std::cout <<
"Displaying image: " << filename << std::endl;
}
private:
void loadFromDisk() const {
std::cout <<
"Loading image: " << filename << std::endl;
}
};
// 代理
class ProxyImage
: public Image {
private:
std::string filename;
mutable RealImage* realImage = nullptr;
public:
explicit ProxyImage(const std::string& filename) : filename(filename) {
}
~ProxyImage() override {
delete realImage;
}
void display() const override {
if (!realImage) {
realImage = new RealImage(filename);
}
realImage->
display();
}
};
// 客户端代码
int main() {
Image* image = new ProxyImage("test.jpg");
// 第一次调用,会加载并显示
image->
display();
std::cout << std::endl;
// 第二次调用,直接显示(已加载)
image->
display();
delete image;
return 0;
}
实现注意事项
- 接口一致性
- 代理必须实现与真实对象相同的接口,确保透明替换
- 生命周期管理
- 代理需要正确管理真实对象的生命周期,避免内存泄漏
- 线程安全
- 在多线程环境中,代理需要处理好并发访问问题
- 避免过度使用
- 仅在确实需要控制访问或增强功能时使用代理模式
常见应用场景
远程代理(Remote Proxy)
- 为位于不同地址空间的对象提供本地接口
- 例如:分布式系统中的远程方法调用(RMI)、Web服务客户端代理
虚拟代理(Virtual Proxy)
- 延迟创建开销大的对象,直到真正需要时才实例化
- 例如:图片懒加载、大型文档的部分内容加载
保护代理(Protection Proxy)
- 控制对敏感对象的访问权限
- 例如:基于角色的访问控制、操作系统文件权限管理
智能引用代理(Smart Reference Proxy)
- 在访问对象时附加额外操作,如引用计数、缓存、资源释放等
- 例如:智能指针、COM组件的生命周期管理
缓存代理(Caching Proxy)
- 缓存频繁访问的对象结果,提高性能
- 例如:数据库查询结果缓存、CDN缓存静态资源
三、优化
- 内存管理:使用智能指针避免内存泄漏
- 线程安全:通过双重检查锁定支持多线程
- 功能扩展:
- 缓存机制减少重复加载
- 日志记录增强可观测性
- 访问控制提升安全性
- 灵活性:
- 工厂模式解耦对象创建
- 代理可组合使用
- 性能优化:
- 延迟加载减少初始开销
- 缓存复用已有资源
用户会话管理系统
#include <iostream>
#include <string>
#include <memory>
#include <unordered_map>
#include <mutex>
#include <chrono>
#include <vector>
// 会话管理系统
class Session
{
public:
virtual std::string getRole() const = 0;
virtual ~Session() = default;
};
class GuestSession
: public Session {
public:
std::string getRole() const override {
return "guest";
}
};
class AdminSession
: public Session {
public:
std::string getRole() const override {
return "admin";
}
};
class SessionManager
{
private:
static SessionManager* instance;
std::unique_ptr<Session> currentSession;
SessionManager() : currentSession(std::make_unique<GuestSession>
()) {
}
SessionManager(const SessionManager&
) = delete;
SessionManager&
operator=(const SessionManager&
) = delete;
public:
static SessionManager* getInstance() {
if (!instance) {
instance = new SessionManager();
}
return instance;
}
void loginAsAdmin() {
std::cout <<
"Logging in as admin..." << std::endl;
currentSession = std::make_unique<AdminSession>
();
}
void logout() {
std::cout <<
"Logging out..." << std::endl;
currentSession = std::make_unique<GuestSession>
();
}
const Session&
getSession() const {
return *currentSession;
}
};
SessionManager* SessionManager::instance = nullptr;
代理模式优化
// 主题接口
class Image
{
public:
virtual void display() const = 0;
virtual ~Image() = default;
};
// 真实主题
class RealImage
: public Image {
private:
std::string filename;
public:
explicit RealImage(const std::string& filename) : filename(filename) {
loadFromDisk();
}
void display() const override {
std::cout <<
"Displaying image: " << filename << std::endl;
}
const std::string&
getFilename() const {
return filename;
}
private:
void loadFromDisk() const {
std::cout <<
"Loading image: " << filename << std::endl;
}
};
// 代理实现
// 延迟加载代理
class ProxyImage
: public Image {
private:
std::string filename;
mutable std::unique_ptr<RealImage> realImage;
public:
explicit ProxyImage(const std::string& filename) : filename(filename) {
}
void display() const override {
if (!realImage) {
std::cout <<
"Creating real image for " << filename << std::endl;
realImage = std::make_unique<RealImage>
(filename);
}
realImage->
display();
}
};
// 缓存代理
class CachedProxyImage
: public Image {
private:
std::string filename;
mutable std::shared_ptr<RealImage> realImage;
static std::unordered_map<std::string, std::weak_ptr<RealImage>> cache;
static std::mutex cacheMutex;
public:
explicit CachedProxyImage(const std::string& filename) : filename(filename) {
}
void display() const override {
std::lock_guard<std::mutex>
lock(cacheMutex);
auto it = cache.find(filename);
if (it != cache.end() &&
!it->second.expired()) {
std::cout <<
"Using cached image: " << filename << std::endl;
realImage = it->second.lock();
} else {
std::cout <<
"Caching new image: " << filename << std::endl;
realImage = std::make_shared<RealImage>
(filename);
cache[filename] = realImage;
}
realImage->
display();
}
};
std::unordered_map<std::string, std::weak_ptr<RealImage>> CachedProxyImage::cache;
std::mutex CachedProxyImage::cacheMutex;
// 访问控制代理
class AuthProxy
: public Image {
private:
std::unique_ptr<Image> target;
std::string requiredRole;
public:
AuthProxy(std::unique_ptr<Image> img, const std::string& role)
: target(std::move(img)), requiredRole(role) {
}
void display() const override {
if (checkAccess()) {
target->
display();
} else {
std::cout <<
"Access denied: Requires role '" << requiredRole
<<
"', current role is '"
<<
SessionManager::getInstance()->
getSession().getRole()
<<
"'" << std::endl;
}
}
private:
bool checkAccess() const {
const auto& role = SessionManager::getInstance()->
getSession().getRole();
return role == requiredRole || role == "admin";
// 管理员可以访问所有资源
}
};
// 日志代理
class LoggingProxy
: public Image {
private:
std::unique_ptr<Image> target;
public:
explicit LoggingProxy(std::unique_ptr<Image> img) : target(std::move(img)) {
}
void display() const override {
std::cout <<
"[LOG] Display request for " <<
getFilename() << std::endl;
auto start = std::chrono::high_resolution_clock::now();
target->
display();
auto end = std::chrono::high_resolution_clock::now();
std::cout <<
"[LOG] Display completed in "
<< std::chrono::duration_cast<std::chrono::milliseconds>
(end - start).count()
<<
"ms" << std::endl;
}
private:
std::string getFilename() const {
if (auto real = dynamic_cast<RealImage*>
(target.get())) {
return real->
getFilename();
}
return "unknown";
}
};
// 工厂类
class ImageFactory
{
public:
static std::shared_ptr<Image>
createProtectedImage(const std::string& filename,
const std::string& role) {
return std::make_shared<LoggingProxy>
(
std::make_unique<AuthProxy>
(
std::make_unique<CachedProxyImage>
(filename),
role
)
);
}
};
// 客户端代码
int main() {
// 创建需要管理员权限的图片代理
auto image = ImageFactory::createProtectedImage("sensitive.jpg", "admin");
// 尝试以访客身份访问(会被拒绝)
std::cout <<
"=== Attempting to access as guest ===" << std::endl;
image->
display();
// 登录为管理员
SessionManager::getInstance()->
loginAsAdmin();
// 再次尝试访问(成功)
std::cout <<
"\n=== Logged in as admin, attempting access ===" << std::endl;
image->
display();
// 再次请求同一图片(使用缓存)
std::cout <<
"\n=== Requesting same image again ===" << std::endl;
image->
display();
// 创建普通用户可见的图片
std::cout <<
"\n=== Creating user-level image ===" << std::endl;
auto userImage = ImageFactory::createProtectedImage("public.jpg", "user");
// 管理员可以访问用户级资源
userImage->
display();
// 登出
SessionManager::getInstance()->
logout();
// 尝试再次访问(被拒绝)
std::cout <<
"\n=== Logged out, attempting access again ===" << std::endl;
image->
display();
userImage->
display();
return 0;
}
优化项
1. 内存管理优化(使用智能指针)
#include <memory>
class ProxyImage
: public Image {
private:
std::string filename;
mutable std::unique_ptr<RealImage> realImage;
// 智能指针自动管理内存
public:
explicit ProxyImage(const std::string& filename) : filename(filename) {
}
void display() const override {
if (!realImage) {
realImage = std::make_unique<RealImage>
(filename);
}
realImage->
display();
}
};
优点:避免内存泄漏,无需手动管理RealImage
生命周期
2. 接口抽象与工厂模式
// 工厂类创建代理
class ImageFactory
{
public:
static std::shared_ptr<Image>
createImage(const std::string& filename) {
return std::make_shared<ProxyImage>
(filename);
}
};
// 客户端使用
auto image = ImageFactory::createImage("test.jpg");
优点:解耦对象创建逻辑,支持未来扩展其他代理类型
3. 多线程安全(双重检查锁定)
void display() const override {
if (!realImage) {
std::lock_guard<std::mutex>
lock(mutex);
// 加锁
if (!realImage) {
// 双重检查
realImage = std::make_unique<RealImage>
(filename);
}
}
realImage->
display();
}
优点:在多线程环境下保证RealImage
只被创建一次
4. 缓存机制(扩展代理功能)
class CachedProxyImage
: public Image {
private:
std::string filename;
mutable std::shared_ptr<RealImage> realImage;
static std::unordered_map<std::string, std::weak_ptr<RealImage>> cache;
static std::mutex cacheMutex;
public:
explicit CachedProxyImage(const std::string& filename) : filename(filename) {
}
void display() const override {
std::lock_guard<std::mutex>
lock(cacheMutex);
// 检查缓存
auto it = cache.find(filename);
if (it != cache.end() &&
!it->second.expired()) {
realImage = it->second.lock();
} else {
realImage = std::make_shared<RealImage>
(filename);
cache[filename] = realImage;
}
realImage->
display();
}
};
优点:相同文件只加载一次,节省资源
5. 延迟加载策略(参数化控制)
class ConfigurableProxy
: public Image {
private:
std::string filename;
std::unique_ptr<RealImage> realImage;
bool lazyLoad;
public:
explicit ConfigurableProxy(const std::string& filename, bool lazy = true)
: filename(filename), lazyLoad(lazy) {
}
void display() const override {
if (!realImage && lazyLoad) {
realImage = std::make_unique<RealImage>
(filename);
}
if (realImage) realImage->
display();
else std::cout <<
"Image not loaded" << std::endl;
}
void forceLoad() {
// 提供显式加载接口
if (!realImage) {
realImage = std::make_unique<RealImage>
(filename);
}
}
};
优点:支持灵活的加载策略配置
6. 日志记录(横切关注点)
class LoggingProxy
: public Image {
private:
std::unique_ptr<Image> target;
public:
explicit LoggingProxy(std::unique_ptr<Image> img) : target(std::move(img)) {
}
void display() const override {
std::cout <<
"[LOG] Display request for " <<
getFilename() << std::endl;
auto start = std::chrono::high_resolution_clock::now();
target->
display();
auto end = std::chrono::high_resolution_clock::now();
std::cout <<
"[LOG] Display completed in "
<< std::chrono::duration_cast<std::chrono::milliseconds>
(end - start).count()
<<
"ms" << std::endl;
}
private:
std::string getFilename() const {
// 通过dynamic_cast尝试获取文件名(需要在RealImage中暴露接口)
if (auto real = dynamic_cast<RealImage*>
(target.get())) {
return real->
getFilename();
}
return "unknown";
}
};
优点:非侵入式添加日志功能
7. 访问控制(保护代理)
class AuthProxy
: public Image {
private:
std::unique_ptr<Image> target;
std::string requiredRole;
public:
AuthProxy(std::unique_ptr<Image> img, const std::string& role)
: target(std::move(img)), requiredRole(role) {
}
void display() const override {
if (checkAccess()) {
target->
display();
} else {
std::cout <<
"Access denied: Requires role " << requiredRole << std::endl;
}
}
private:
bool checkAccess() const {
// 实际实现中会检查当前用户角色
// 这里简化为硬编码检查
return UserSession::getCurrentRole() == requiredRole;
}
};
优点:实现细粒度的访问控制
8. 组合多种代理
// 客户端可以组合使用多种代理
auto image = std::make_unique<LoggingProxy>
(
std::make_unique<AuthProxy>
(
std::make_unique<ProxyImage>
("secret.jpg"),
"admin"
)
);
image->
display();
// 先验证权限,再记录日志,最后显示图片
优点:灵活组合不同功能的代理
四、与其他模式的区别
与装饰器模式的区别
- 代理模式:重点在于控制对象的访问,客户端通常不知道真实对象的存在
- 装饰器模式:重点在于动态增强对象的功能,客户端直接与装饰器交互
与适配器模式的区别
- 代理模式:保持接口不变,提供相同功能但增加控制逻辑
- 适配器模式:改变接口以适配不同的客户端需求