代理模式通过代理对象控制对真实对象的访问,实现权限管理、延迟加载等功能。在C++中,代理与真实对象共同实现同一接口,客户端通过接口与代理交互,代理可在调用真实对象前后执行权限检查、日志记录等操作。示例中DocumentProxy基于角色进行权限控制,支持延迟加载,体现了代理模式在访问控制、资源优化和安全增强方面的优势。相较于装饰器、适配器等模式,代理的核心在于访问控制而非功能增强或接口转换。实现代理时需注意生命周期管理、性能开销、线程安全等问题,避免过度设计。

C++中的代理模式,说白了,就是给你想访问的那个对象(我们叫它“真实对象”)套上一个“马甲”或者说“门卫”。这个“门卫”就是代理对象。它的核心作用,就是让你不直接接触真实对象,而是通过它来间接访问。在这个过程中,代理可以决定你能不能访问,能访问哪些部分,甚至可以在你访问前后做一些额外的事情,比如权限检查、日志记录、延迟加载等等。它提供了一种非常优雅且非侵入性的方式,来控制、增强或限制真实对象的行为,而无需改动真实对象本身的任何代码。
在C++里,实现代理模式通常需要一个抽象接口,真实对象和代理对象都实现这个接口。这样,客户端代码就可以统一地通过这个接口来操作,而不需要关心它背后是真实对象还是代理对象。这正是多态的魅力所在。
设想我们有一个
Document
IDocument
RealDocument
DocumentProxy
IDocument
#include <iostream>
#include <string>
#include <map> // 用于模拟用户权限
// 抽象接口:定义真实对象和代理对象的公共接口
class IDocument {
public:
virtual ~IDocument() = default;
virtual std::string getContent() const = 0;
virtual void editContent(const std::string& newContent) = 0;
virtual void printDocument() const = 0;
};
// 真实对象:包含实际业务逻辑和数据
class RealDocument : public IDocument {
private:
std::string content;
std::string documentName; // 假设文档有名称
public:
RealDocument(const std::string& name, const std::string& initialContent)
: documentName(name), content(initialContent) {
std::cout << "RealDocument '" << documentName << "' created." << std::endl;
}
~RealDocument() {
std::cout << "RealDocument '" << documentName << "' destroyed." << std::endl;
}
std::string getContent() const override {
return content;
}
void editContent(const std::string& newContent) override {
content = newContent;
std::cout << "RealDocument '" << documentName << "' content updated." << std::endl;
}
void printDocument() const override {
std::cout << "Printing RealDocument '" << documentName << "':\n" << content << std::endl;
}
};
// 代理对象:控制对真实对象的访问,并实现权限管理
class DocumentProxy : public IDocument {
private:
RealDocument* realDocument; // 持有真实对象的指针
std::string documentName;
std::string currentUserRole; // 模拟当前用户的角色
// 模拟权限表
static std::map<std::string, std::map<std::string, bool>> permissions;
// 延迟加载标志
bool isLoaded;
// 内部方法,用于延迟加载真实对象
void ensureDocumentLoaded() const {
if (!isLoaded) {
std::cout << "Proxy: Lazily loading RealDocument '" << documentName << "'..." << std::endl;
// 实际应用中,这里可能是从文件系统或数据库加载内容
const_cast<DocumentProxy*>(this)->realDocument = new RealDocument(documentName, "Default content for " + documentName);
const_cast<DocumentProxy*>(this)->isLoaded = true;
}
}
// 检查权限的辅助函数
bool checkPermission(const std::string& operation) const {
if (permissions.count(currentUserRole) && permissions[currentUserRole].count(operation)) {
return permissions[currentUserRole][operation];
}
std::cerr << "Proxy: Permission denied for role '" << currentUserRole << "' to perform '" << operation << "'." << std::endl;
return false;
}
public:
DocumentProxy(const std::string& name, const std::string& role)
: realDocument(nullptr), documentName(name), currentUserRole(role), isLoaded(false) {
std::cout << "DocumentProxy for '" << documentName << "' created for role '" << currentUserRole << "'." << std::endl;
}
~DocumentProxy() {
if (realDocument) {
delete realDocument;
realDocument = nullptr;
}
std::cout << "DocumentProxy for '" << documentName << "' destroyed." << std::endl;
}
// 实现IDocument接口方法,并在其中加入权限检查和延迟加载
std::string getContent() const override {
if (!checkPermission("read")) {
return "Access Denied.";
}
ensureDocumentLoaded();
return realDocument->getContent();
}
void editContent(const std::string& newContent) override {
if (!checkPermission("write")) {
std::cerr << "Proxy: Cannot edit. Write permission denied." << std::endl;
return;
}
ensureDocumentLoaded();
realDocument->editContent(newContent);
}
void printDocument() const override {
if (!checkPermission("print")) {
std::cerr << "Proxy: Cannot print. Print permission denied." << std::endl;
return;
}
ensureDocumentLoaded();
realDocument->printDocument();
}
// 初始化静态权限表
static void initializePermissions() {
// 管理员可以读写打印
permissions["admin"]["read"] = true;
permissions["admin"]["write"] = true;
permissions["admin"]["print"] = true;
// 编辑员可以读写,不能打印
permissions["editor"]["read"] = true;
permissions["editor"]["write"] = true;
permissions["editor"]["print"] = false;
// 访客只能读
permissions["guest"]["read"] = true;
permissions["guest"]["write"] = false;
permissions["guest"]["print"] = false;
}
};
// 静态成员初始化
std::map<std::string, std::map<std::string, bool>> DocumentProxy::permissions;
// 客户端代码
int main() {
DocumentProxy::initializePermissions();
std::cout << "--- Admin User ---" << std::endl;
DocumentProxy adminDoc("Report.pdf", "admin");
std::cout << "Admin reads: " << adminDoc.getContent() << std::endl; // 第一次访问会加载真实对象
adminDoc.editContent("Admin updated report content.");
adminDoc.printDocument();
std::cout << std::endl;
std::cout << "--- Editor User ---" << std::endl;
DocumentProxy editorDoc("Report.pdf", "editor");
std::cout << "Editor reads: " << editorDoc.getContent() << std::endl;
editorDoc.editContent("Editor added some notes.");
editorDoc.printDocument(); // 应该会被拒绝
std::cout << std::endl;
std::cout << "--- Guest User ---" << std::endl;
DocumentProxy guestDoc("Report.pdf", "guest");
std::cout << "Guest reads: " << guestDoc.getContent() << std::endl;
guestDoc.editContent("Guest tried to edit."); // 应该会被拒绝
guestDoc.printDocument(); // 应该会被拒绝
std::cout << std::endl;
return 0;
}在这个例子里,
DocumentProxy
RealDocument
立即学习“C++免费学习笔记(深入)”;
在C++中,利用代理模式进行权限管理,其实有很多种玩法,不只是简单地
if (hasPermission)
1. 基于角色的访问控制 (RBAC - Role-Based Access Control): 这是最直观也最常用的方式。代理对象内部维护一个用户角色(比如“管理员”、“编辑”、“访客”),然后根据这个角色去查一个权限表,看当前角色有没有执行某个操作(读、写、打印等)的权限。就像上面代码里展示的那样,
DocumentProxy
currentUserRole
2. 基于属性的访问控制 (ABAC - Attribute-Based Access Control): ABAC比RBAC更灵活,它不只看用户的角色,还会综合考虑用户本身的属性(比如部门、级别)、资源本身的属性(比如文档的密级、所有者)、甚至环境属性(比如访问时间、IP地址)来动态地决定是否授权。代理模式在这种场景下,会在权限检查逻辑中引入更多的上下文信息。例如,
DocumentProxy
editContent
if (currentUser.department == document.ownerDepartment && currentUser.level >= document.requiredLevel)
3. 黑名单/白名单机制: 代理可以维护一个不允许执行操作的“黑名单”列表,或者只允许执行操作的“白名单”列表。例如,
DocumentProxy
blockUser(userId)
4. 组合策略: 实际项目中,往往不是单一策略就能搞定的。比如,你可以先用RBAC做大范围的权限划分,然后对某些敏感操作或资源,再叠加ABAC进行更细致的校验。代理模式的优势在于,它提供了一个统一的入口,你可以在这个入口处灵活地组合各种权限检查逻辑,而不需要真实对象知道这些复杂的权限规则。我个人觉得,在设计权限系统时,总是要从最简单够用的方案开始,然后根据实际需求逐步增加复杂度,代理模式在这里就提供了一个很好的扩展点。
代理模式在控制对象访问方面确实有其独到之处,它和一些其他设计模式(比如装饰器、适配器、外观模式)虽然表面上看起来有些相似,但核心意图和适用场景却大相径庭。
独特优势:
RealDocument
getContent()
editContent()
适用场景:
相比之下,装饰器模式(Decorator)更侧重于动态地给对象添加新功能,它强调的是“增强”;适配器模式(Adapter)则主要解决接口不兼容的问题,让两个原本不兼容的接口能够协同工作;外观模式(Facade)则是为子系统提供一个统一的接口,简化客户端与复杂子系统的交互。代理模式的核心在于“控制访问”,这是它与其他模式最本质的区别。
C++里实现代理模式,虽然设计思想上很优雅,但在具体落地时,确实有一些坑需要注意,尤其是在性能和工程实践方面。我个人在项目中就踩过一些,这里总结一下:
常见陷阱:
delete
std::unique_ptr
std::shared_ptr
性能考量:
解决这些问题,通常需要我们在设计时进行权衡。对于性能敏感的部分,可能需要避免代理,或者采用更轻量级的代理实现。对于生命周期管理,智能指针几乎是必选项。同时,保持代理的职责单一,避免它承担过多不相关的任务,也能有效降低复杂性。
以上就是C++代理模式控制对象访问与权限管理的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号