当前位置: 首页 > news >正文

设计模式(C++)详解——代理模式 (Proxy Pattern)(1)

<摘要>

代理模式是一种结构型设计模式,其核心意图是为其他对象提供一种代理以控制对这个对象的访问。它通过引入一个与被代理对象实现相同接口的代理类,在客户端和目标对象之间架设一道中间层,从而实现对目标对象访问的间接控制。这种控制可以体现在多个方面:延迟加载高开销对象(虚拟代理)、添加权限验证(保护代理)、简化复杂系统调用(远程代理)、或管理资源生命周期(智能引用代理)。代理模式是面向对象设计中“开闭原则”和“单一职责原则”的经典体现,它将核心业务功能与辅助性管控逻辑分离,使得系统更易于扩展和维护。在现代软件开发中,从ORM框架的延迟加载到分布式系统的RPC stub,再到C++的智能指针,代理模式的应用无处不在,是构建灵活、健壮软件架构的重要工具之一。


<解析>

1. 背景与核心概念

1.1 起源与发展

代理模式的概念并非凭空产生,它源于现实世界以及早期计算机科学中对“中介”或“代表”的抽象需求。在法律领域,律师是当事人的代理;在商业中,经纪人是明星或作家的代理。这些代理负责处理访问、谈判、日程安排等事务,从而保护或简化被代理对象的职责。

在软件领域,代理模式的形式化描述最早出现在著名的“四人帮”(GoF)的著作《设计模式:可复用面向对象软件的基础》(1994年)中,并被归类为结构型模式。其设计初衷是为了解决直接访问某些对象时带来的问题,例如:

  • 性能问题:某些对象创建开销极大,不需要在程序启动时就完全初始化。
  • 权限问题:客户端的访问需要被验证和授权。
  • 复杂性隐藏:对象位于远程地址空间或复杂子系统之后,直接访问不便。
  • 增强功能:需要在对象访问前后添加额外的逻辑(如日志、引用计数)。

随着软件架构的发展,尤其是分布式系统(如CORBA, Java RMI, Web Services)和大型单体应用(如基于ORM的企业应用)的兴起,代理模式的应用变得更加广泛和关键。如今,它已是众多框架和库(如Spring AOP, Hibernate, STL)的基石之一。

1.2 核心概念与术语

代理模式的核心在于“控制访问”,而非“增强功能”(这与装饰器模式不同)。它包含三个核心角色,其UML类图如下所示:

uses
«interface»
Subject
+Request()
RealSubject
+Request()
Proxy
-realSubject: RealSubject
+Request()
  • Subject (抽象主题角色)

    • 定义RealSubjectProxy的共用接口。
    • 这样,任何使用RealSubject的地方都可以透明地使用Proxy
    • 通常以抽象类或接口的形式存在。
  • RealSubject (真实主题角色)

    • 定义了代理所代表的真实对象。
    • 是最终要引用的对象,包含了业务逻辑的核心功能。
    • 客户端通常直接调用其方法以完成实际工作。
  • Proxy (代理主题角色)

    • 持有对RealSubject的引用(或可以创建/获取它)。
    • 实现与RealSubject相同的接口(继承自Subject)。
    • 在调用RealSubjectRequest()方法前后,可以执行额外的控制操作,如延迟加载、访问控制、日志记录等。

关键术语:

  • 透明性 (Transparency):客户端认为它是在直接操作RealSubject,并不知道代理的存在。这是代理模式追求的理想效果。
  • 间接性 (Indirection):代理在客户端和真实对象之间提供了一层间接层,这层间接层是所有控制逻辑发生的地方。
1.3 代理类型

根据目的和场景的不同,代理模式有多种常见的实现类型:

代理类型目的典型应用场景
虚拟代理 (Virtual Proxy)延迟昂贵对象的创建或加载,直到真正需要它时。大型图片的延迟加载、ORM框架中的延迟加载(Lazy Loading)。
保护代理 (Protection Proxy)控制对真实对象的访问权限。基于角色的访问控制(RBAC)系统。
远程代理 (Remote Proxy)为位于不同地址空间(远程服务器)的对象提供本地代表。RPC(远程过程调用)、RMI(远程方法调用)、Web Service客户端存根(Stub)。
智能引用代理 (Smart Reference)在访问对象时执行额外的内务处理操作。智能指针(如std::shared_ptr)、对象引用计数、线程安全控制、懒初始化。
缓存代理 (Cache Proxy)为开销大的运算结果提供临时存储。Web服务器反向代理缓存、数据库查询缓存。

2. 设计意图与考量

2.1 核心目标与设计理念

代理模式的根本意图是:在不直接操作目标对象的前提下,通过一个代理对象来间接地控制和管理对该目标对象的访问

其背后的设计理念遵循了多个面向对象设计原则:

  1. 开闭原则 (Open/Closed Principle):客户端代码依赖于抽象的Subject接口,对扩展开放(可以引入新的代理类型),对修改关闭(无需修改操作Subject的客户端代码)。
  2. 单一职责原则 (Single Responsibility Principle):将“管理目标对象的访问逻辑”与“目标对象自身的核心业务逻辑”分离。RealSubject只关心如何完成工作,而Proxy则关心何时、以何种条件、何种方式去使用RealSubject
  3. 最少知识原则 (Law of Demeter):客户端只需与代理交互,而不需要了解背后真实对象和访问控制逻辑的复杂细节,降低了耦合度。
2.2 权衡因素

引入代理模式并非没有代价,设计时需要权衡以下因素:

  • 优点

    • 职责清晰:隔离了控制逻辑和业务逻辑,系统更具可维护性。
    • 高扩展性:可以轻松地增加新的代理,而不影响客户端和真实主题。
    • 更好的安全性和灵活性:通过保护代理可以方便地增加权限管理。
    • 性能优化:通过虚拟代理和缓存代理可以提升系统整体性能。
  • 缺点

    • 系统复杂度增加:引入了新的代理层,可能会使系统设计更复杂。
    • 请求速度可能变慢:由于增加了中间层,请求的转发和处理会导致轻微的延迟。但在虚拟代理等场景下,整体性能反而是提升的。
    • 开发难度:需要为服务接口创建代理类,特别是在没有代码生成工具辅助时,工作量会增加。

与装饰器模式的区别
这是一个常见的困惑点。两者在结构上非常相似,但意图不同

  • 代理模式的目的是控制访问。代理通常知道被代理对象的生命周期,并决定是否、何时创建和调用它。关系通常是确定的(一个代理对应一个真实主题)。
  • 装饰器模式的目的是增强功能。装饰器为对象添加新的职责,它对被装饰对象是透明的。装饰器可以层层嵌套,动态地添加任意多个功能。

简单来说:代理是“经纪人”,控制你见不见明星;装饰是“造型师”,负责给明星(或另一个造型师)做造型。

3. 实例与应用场景

下面我们通过三个代表性的案例来具体说明代理模式的应用。

案例一:虚拟代理实现大型图片延迟加载

应用场景:在图形界面应用程序中,如果一个窗口包含多张大型图片,一次性加载所有图片会导致启动缓慢,用户体验差。我们希望仅当图片需要显示(滚动到视口内)时,才从磁盘加载它。

#include <iostream>
#include <string>
#include <memory>// 1. 抽象主题角色 (Subject)
class Image {
public:virtual ~Image() = default;virtual void display() = 0; // 显示图片的通用接口
};// 2. 真实主题角色 (RealSubject)
class RealImage : public Image {
public:RealImage(const std::string& filename) : m_filename(filename) {loadFromDisk(); // 构造函数中直接加载,开销大}void display() override {std::cout << "Displaying " << m_filename << std::endl;}private:void loadFromDisk() {std::cout << "Loading... " << m_filename << std::endl; // 模拟耗时的磁盘I/O操作// ... 实际从磁盘加载图片数据的代码 ...}std::string m_filename;
};// 3. 代理角色 (Proxy)
class ProxyImage : public Image {
public:ProxyImage(const std::string& filename) : m_filename(filename), m_realImage(nullptr) {// 代理构造时,并不立即创建真实的图片对象std::cout << "Proxy created for " << m_filename << std::endl;}~ProxyImage() {delete m_realImage; // 释放资源}void display() override {// 延迟初始化:直到真正需要显示时,才创建真实对象if (m_realImage == nullptr) {m_realImage = new RealImage(m_filename);}// 调用真实对象的显示方法m_realImage->display();}private:std::string m_filename;RealImage* m_realImage; // 持有对真实对象的引用
};// 客户端代码
int main() {// 创建代理时,RealImage还未创建,启动快ProxyImage proxyImage1("high_res_photo1.jpg");ProxyImage proxyImage2("high_res_photo2.jpg");// 模拟用户交互,只有需要显示时才加载真实图片std::cout << "--- User scrolls to see image1 ---" << std::endl;proxyImage1.display(); // 此时才会加载第一张图片std::cout << "--- User scrolls to see image2 ---" << std::endl;proxyImage2.display(); // 此时才会加载第二张图片// 再次显示,不会再加载,直接使用已创建的对象std::cout << "--- User scrolls back to image1 ---" << std::endl;proxyImage1.display();return 0;
}

输出结果:

Proxy created for high_res_photo1.jpg
Proxy created for high_res_photo2.jpg
--- User scrolls to see image1 ---
Loading... high_res_photo1.jpg
Displaying high_res_photo1.jpg
--- User scrolls to see image2 ---
Loading... high_res_photo2.jpg
Displaying high_res_photo2.jpg
--- User scrolls back to image1 ---
Displaying high_res_photo1.jpg

流程时序图:

ClientProxyRealImageProxyImage("photo1.jpg")创建代理,RealImage未初始化display()new RealImage("photo1.jpg")耗时:从磁盘加载display()display() // 第二次调用display() // 直接调用已存在对象ClientProxyRealImage
案例二:保护代理实现权限控制

应用场景:在一个文档管理系统中,不同的用户对文档(Document)拥有不同的操作权限(如查看、修改)。我们需要在执行操作前验证当前用户的权限。

#include <iostream>
#include <string>// 1. 抽象主题角色
class Document {
public:virtual ~Document() = default;virtual void view() = 0;   // 查看文档virtual void edit() = 0;   // 编辑文档
};// 2. 真实主题角色
class RealDocument : public Document {
public:RealDocument(const std::string& title, const std::string& content): m_title(title), m_content(content) {}void view() override {std::cout << "Viewing Document: " << m_title << std::endl;std::cout << "Content: " << m_content << std::endl;}void edit() override {std::cout << "Editing Document: " << m_title << std::endl;std::cout << "Please enter new content: ";std::getline(std::cin, m_content); // 模拟编辑操作std::cout << "Document saved." << std::endl;}private:std::string m_title;std::string m_content;
};// 3. 代理角色 (保护代理)
class ProtectionProxy : public Document {
public:// 构造函数传入真实对象和当前用户角色ProtectionProxy(std::shared_ptr<RealDocument> doc, const std::string& userRole): m_realDocument(doc), m_userRole(userRole) {}void view() override {// 任何人都可以查看std::cout << "[Protection Proxy] Permission check for 'view'... ";if (hasPermission("view")) {std::cout << "Granted." << std::endl;m_realDocument->view();} else {std::cout << "Denied." << std::endl;std::cout << "You need 'view' permission." << std::endl;}}void edit() override {// 只有编辑者或管理员可以编辑std::cout << "[Protection Proxy] Permission check for 'edit'... ";if (hasPermission("edit")) {std::cout << "Granted." << std::endl;m_realDocument->edit();} else {std::cout << "Denied." << std::endl;std::cout << "You need 'edit' permission." << std::endl;}}private:bool hasPermission(const std::string& action) const {// 简单的权限验证逻辑if (action == "view") {return true; // 所有人都可以查看} else if (action == "edit") {return (m_userRole == "editor" || m_userRole == "admin");}return false;}std::shared_ptr<RealDocument> m_realDocument;std::string m_userRole;
};// 客户端代码
int main() {// 创建一个真实文档auto realDoc = std::make_shared<RealDocument>("Annual Report", "Confidential data...");// 为不同用户创建代理ProtectionProxy readerProxy(realDoc, "reader");ProtectionProxy editorProxy(realDoc, "editor");std::cout << "=== Reader trying to access ===" << std::endl;readerProxy.view();readerProxy.edit(); // 这里会被拒绝std::cout << "\n=== Editor trying to access ===" << std::endl;editorProxy.view();editorProxy.edit(); // 这里会被允许return 0;
}

输出结果:

=== Reader trying to access ===
[Protection Proxy] Permission check for 'view'... Granted.
Viewing Document: Annual Report
Content: Confidential data...
[Protection Proxy] Permission check for 'edit'... Denied.
You need 'edit' permission.=== Editor trying to access ===
[Protection Proxy] Permission check for 'view'... Granted.
Viewing Document: Annual Report
Content: Confidential data...
[Protection Proxy] Permission check for 'edit'... Granted.
Editing Document: Annual Report
Please enter new content: [用户输入...]
Document saved.
案例三:智能指针 -

应用场景:C++中原始指针的管理需要开发者手动newdelete,极易导致内存泄漏、悬空指针等问题。智能指针(如std::shared_ptr)是代理模式的经典应用,它代理了原始指针,通过引用计数自动管理生命周期。

std::shared_ptr本身就是一个复杂的库实现,但其核心思想可以用一个简化的版本来说明:

#include <iostream>// 简化的智能指针代理模板类 (RefCountedSmartProxy)
template<typename T>
class SmartPtr {
public:// 构造函数,获取资源explicit SmartPtr(T* ptr = nullptr) : m_ptr(ptr), m_count(new size_t(1)) {std::cout << "SmartPtr Constructor. Ref count: " << *m_count << std::endl;}// 拷贝构造函数,共享资源,增加引用计数SmartPtr(const SmartPtr<T>& other) : m_ptr(other.m_ptr), m_count(other.m_count) {(*m_count)++;std::cout << "SmartPtr Copy Constructor. Ref count: " << *m_count << std::endl;}// 析构函数,减少引用计数,计数为0时释放资源~SmartPtr() {(*m_count)--;std::cout << "SmartPtr Destructor. Ref count: " << *m_count << std::endl;if (*m_count == 0) {delete m_ptr;delete m_count;std::cout << "Resource released." << std::endl;}}// 重载 -> 和 * 运算符,使代理对象用起来像原始指针一样T* operator->() const { return m_ptr; }T& operator*() const { return *m_ptr; }// 获取当前引用计数size_t use_count() const { return *m_count; }private:T* m_ptr;           // 代理的原始指针size_t* m_count;    // 指向引用计数的指针
};// 一个简单的资源类
class ExpensiveResource {
public:ExpensiveResource() { std::cout << "ExpensiveResource Created." << std::endl; }~ExpensiveResource() { std::cout << "ExpensiveResource Destroyed." << std::endl; }void doSomething() { std::cout << "Doing something..." << std::endl; }
};// 客户端代码
int main() {std::cout << "--- Block 1 Start ---" << std::endl;{// 创建一个智能代理,管理一个新资源SmartPtr<ExpensiveResource> ptr1(new ExpensiveResource());ptr1->doSomething(); // 通过代理使用资源,如同使用原始指针std::cout << "--- Block 2 Start ---" << std::endl;{// 拷贝构造,共享资源SmartPtr<ExpensiveResource> ptr2 = ptr1;ptr2->doSomething();std::cout << "Use count: " << ptr2.use_count() << std::endl; // 应该是2} // ptr2析构,计数减1std::cout << "--- Block 2 End ---" << std::endl;std::cout << "Use count: " << ptr1.use_count() << std::endl; // 应该是1ptr1->doSomething();} // ptr1析构,计数减为0,资源被释放std::cout << "--- Block 1 End ---" << std::endl;return 0;
}

输出结果:

--- Block 1 Start ---
ExpensiveResource Created.
SmartPtr Constructor. Ref count: 1
Doing something...
--- Block 2 Start ---
SmartPtr Copy Constructor. Ref count: 2
Doing something...
Use count: 2
SmartPtr Destructor. Ref count: 1
--- Block 2 End ---
Use count: 1
Doing something...
SmartPtr Destructor. Ref count: 0
Resource released.
ExpensiveResource Destroyed.
--- Block 1 End ---

这个简化的SmartPtr代理了ExpensiveResource对象的指针,通过重载->*运算符,使得客户端可以像使用原始指针一样使用它。但其内部实现了引用计数,自动管理内存的释放,完美诠释了智能引用代理的角色。

4. 代码实现与编译运行

我们将以案例一(虚拟代理)的代码为例,提供完整的工程化实现,包括注释、流程图和Makefile。

4.1 带完整注释的代码实现

virtual_proxy.h

/*** @file virtual_proxy.h* @brief 虚拟代理模式示例:图片延迟加载* * 定义了Image接口、RealImage实现类和ProxyImage代理类。* 代理类负责控制RealImage对象的创建时机,实现延迟加载。*/#ifndef VIRTUAL_PROXY_H
#define VIRTUAL_PROXY_H#include <iostream>
#include <string>/*** @brief 抽象图像接口 (Subject)* * 定义客户端使用的通用图像操作接口,RealImage和ProxyImage都实现此接口。*/
class Image {
public:virtual ~Image() = default;/*** @brief 显示图像* * 纯虚函数,具体由子类实现显示逻辑。*/virtual void display() = 0;
};/*** @brief 真实图像类 (RealSubject)* * 代表实际的高开销图像对象,在构造时即从磁盘加载图像数据。*/
class RealImage : public Image {
public:/*** @brief 构造函数* @param filename 图像文件名* * 构造RealImage对象并立即调用loadFromDisk加载图像数据。*/explicit RealImage(const std::string& filename);/*** @brief 显示图像* * 将已加载的图像数据渲染到屏幕上(此处模拟输出)。*/void display() override;private:/*** @brief 从磁盘加载图像* * 模拟一个耗时且资源密集型的磁盘I/O操作。* 此函数由构造函数调用,不应由客户端直接调用。*/void loadFromDisk();std::string m_filename; ///< 图像文件路径
};/*** @brief 图像代理类 (Proxy)* * 作为RealImage的代理,控制对其的访问。* 实现延迟加载:仅在第一次调用display()时创建RealImage对象。*/
class ProxyImage : public Image {
public:/*** @brief 构造函数* @param filename 图像文件名* * 仅存储文件名,并不立即创建或加载RealImage对象,启动速度快。*/explicit ProxyImage(const std::string& filename);/*** @brief 析构函数* * 负责释放所管理的RealImage对象(如果已创建)。*/~ProxyImage();/*** @brief 显示图像* * 首次调用时,会创建并加载RealImage对象,后续调用直接使用已加载的对象。*/void display() override;private:std::string m_filename;   ///< 图像文件路径RealImage* m_realImage;   ///< 指向真实图像对象的指针
};#endif // VIRTUAL_PROXY_H

virtual_proxy.cpp

/*** @file virtual_proxy.cpp* @brief 虚拟代理模式示例的实现文件*/#include "virtual_proxy.h"// RealImage 实现
RealImage::RealImage(const std::string& filename) : m_filename(filename) {loadFromDisk();
}void RealImage::display() {std::cout << "Displaying image: " << m_filename << std::endl;
}void RealImage::loadFromDisk() {std::cout << "Loading heavy image from disk: " << m_filename << " (This operation is expensive!)" << std::endl;// 模拟耗时操作,例如:// for (int i = 0; i < 1000000000; ++i) {} // 空循环模拟耗时// 实际代码中可能是: m_imageData = stbi_load(filename, ...);
}// ProxyImage 实现
ProxyImage::ProxyImage(const std::string& filename) : m_filename(filename), m_realImage(nullptr) {std::cout << "Proxy created for image: " << m_filename << " (RealImage not loaded yet)" << std::endl;
}ProxyImage::~ProxyImage() {delete m_realImage; // Safe to delete nullptr
}void ProxyImage::display() {// 延迟初始化 (Lazy Initialization)if (m_realImage == nullptr) {std::cout << "Proxy: First time display requested. Creating RealImage..." << std::endl;m_realImage = new RealImage(m_filename);} else {std::cout << "Proxy: RealImage already exists. Reusing it." << std::endl;}// 委托给真实对象处理请求m_realImage->display();
}

main.cpp

/*** @file main.cpp* @brief 客户端代码,演示虚拟代理的使用* * 创建多个图像代理,模拟用户滚动查看图像的场景,展示延迟加载的效果。*/#include "virtual_proxy.h"
#include <vector>int main() {std::cout << "Application started. Creating image proxies (very fast)..." << std::endl;// 创建图像代理列表 - 此时不会加载任何真实图像std::vector<ProxyImage> imageProxies;imageProxies.emplace_back("vacation_photo_1.jpg");imageProxies.emplace_back("vacation_photo_2.jpg");imageProxies.emplace_back("vacation_photo_3.jpg");std::cout << "\n--- User scrolls to see the first image ---" << std::endl;imageProxies[0].display(); // 第一次显示,触发加载std::cout << "\n--- User scrolls to see the third image ---" << std::endl;imageProxies[2].display(); // 第一次显示,触发加载std::cout << "\n--- User scrolls back to the first image ---" << std::endl;imageProxies[0].display(); // 再次显示,使用已加载对象std::cout << "\nApplication shutdown." << std::endl;return 0;
}
4.2 流程图与编译运行

流程图:
下面的流程图描述了代理模式在客户端请求时的决策和执行流程。

客户端调用 proxy.display
RealImage 是否存在?
创建 RealImage 对象
从磁盘加载图像数据
调用 realImage.display
图像显示完成

Makefile 范例:

# Compiler and flags
CXX := g++
CXXFLAGS := -std=c++17 -Wall -Wextra -O2# Targets
TARGET := virtual_proxy_demo
SRCS := main.cpp virtual_proxy.cpp
OBJS := $(SRCS:.cpp=.o)
HEADERS := virtual_proxy.h.PHONY: all cleanall: $(TARGET)$(TARGET): $(OBJS)$(CXX) $(CXXFLAGS) -o $@ $^%.o: %.cpp $(HEADERS)$(CXX) $(CXXFLAGS) -c $< -o $@clean:rm -f $(TARGET) $(OBJS)# 运行目标
run: $(TARGET)./$(TARGET)

编译与运行方法:

  1. 保存文件:将上述代码分别保存为 virtual_proxy.h, virtual_proxy.cpp, main.cppMakefile,放在同一目录下。
  2. 编译项目:打开终端,导航到该目录,执行 make 命令。
    $ make
    g++ -std=c++17 -Wall -Wextra -O2 -c main.cpp -o main.o
    g++ -std=c++17 -Wall -Wextra -O2 -c virtual_proxy.cpp -o virtual_proxy.o
    g++ -std=c++17 -Wall -Wextra -O2 -o virtual_proxy_demo main.o virtual_proxy.o
    
    这将生成可执行文件 virtual_proxy_demo
  3. 运行程序:执行 make run 或直接运行生成的可执行文件。
    $ ./virtual_proxy_demo
    
  4. 结果解读
    观察输出,你可以清晰地看到:
    • 程序启动时,只创建了代理对象(Proxy created...),速度很快。
    • 当第一次请求显示某张图片时(First time display requested...),代理才去创建并加载真实的RealImage对象(Loading heavy image...)。
    • 再次请求显示同一张图片时,代理会直接使用已创建的对象(RealImage already exists...),避免了重复的昂贵加载操作。
      这完美验证了虚拟代理“延迟加载,优化性能”的设计目标。

5. 交互性内容解析(以远程代理为例)

远程代理是代理模式在处理网络通信时的典型应用。它通常涉及两个部分:

  1. 客户端存根 (Stub):位于客户端本地,代理远程对象。它负责将本地的方法调用序列化成网络报文(Marshalling),并通过网络发送给服务器。
  2. 服务器骨架 (Skeleton):位于服务器端,接收客户端请求,反序列化报文(Unmarshalling),调用真实的远程对象方法,再将结果序列化并传回客户端。

其交互时序图如下:

ClientProxy(Stub)NetworkSkeletonRealObjectcall method()1. 序列化参数2. 组织请求报文send(request message)request message3. 反序列化参数4. 调用真实对象method(args)result5. 序列化结果6. 组织响应报文send(response message)response message7. 反序列化结果return resultClientProxy(Stub)NetworkSkeletonRealObject

报文结构示例(简化):
一个简单的RPC请求报文可能包含以下信息:

| Magic Number (2 bytes) | Version (1 byte) | Message Type (1 byte) | Request ID (4 bytes) | Method Name Length (2 bytes) | Method Name (UTF-8 String) | Payload Length (4 bytes) | Payload (Serialized Parameters) |
  • Magic Number:标识协议,例如 0xCAFE
  • Message Type:区分是请求(0x01)还是响应(0x02)。
  • Request ID:用于匹配请求和响应。
  • Method Name:客户端要调用的远程方法名。
  • Payload:方法的参数,通常使用JSON、Protobuf、MsgPack等格式序列化。

客户端存根(代理)的工作就是填充这个报文并发送,服务器骨架则解析这个报文,找到对应的真实方法并调用。对客户端而言,它就像在调用本地方法一样,完全感知不到网络通信的存在。这就是远程代理实现的“位置透明性”。

总结

代理模式是一种强大而灵活的结构型设计模式,它通过引入一个代理对象来控制对另一个对象的访问。这种控制带来了诸多好处,包括延迟加载、权限控制、简化复杂访问以及智能资源管理等。其核心在于添加一层间接性,这符合许多软件设计原则,使得系统更加清晰、可扩展和可维护。

从简单的虚拟代理图片,到复杂的RPC框架远程代理,再到C++标准库中的智能指针,代理模式的应用几乎无处不在。理解和掌握代理模式,不仅能帮助我们写出更优雅、高效的代码,更能让我们深刻体会到面向对象设计中“间接层”的艺术和威力。当您下一次看到std::shared_ptr或者Hibernate的懒加载时,您会心一笑,因为您知道,这背后正是代理模式在默默地发挥着作用。

http://www.dtcms.com/a/394682.html

相关文章:

  • 聊聊AI agents MCP 开发
  • 【C++进阶】智能指针的使用及其原理
  • 极客天成让统一存储从云原生‘进化’到 AI 原生: 不是版本升级,而是基因重组
  • 【JavaScript 性能优化实战】第五篇:运行时性能优化进阶(懒加载 + 预加载 + 资源优先级)
  • Java基础(十二):抽象类与接口详解
  • 使用《微PE》软件,制作U盘启动盘(PE工具盘)
  • 《初阶 Linux 工具学习:Shell运行原理以及Linux权限讲解》
  • 树链剖分(模板 + 思路)
  • 医疗数据互操作性与联邦学习的python编程方向研究(上)
  • Windows最新摆烂更新,让用户没法看视频了
  • 可配置化App启动弹窗系统:实现后台动态管理与热更新引导-蜻蜓Q系统laravel+vue3-优雅草卓伊凡
  • Permute 媒体文件格式转换【音视频图像文件转换】(Mac电脑)
  • Netty:实现RPC服务(实战)
  • 408复习笔记—MIPS指令系统
  • 阿里万相2.1:蓝耘MaaS平台部署 vs 官网在线使用:万字实测对比与深度技术解析
  • 11月长春EI会议:ISRAI 2025 诚邀学者参与投稿
  • 【AI时代速通QT】第七节:Visual Studio+Qt 开发指南
  • 医疗问诊陪诊小程序:全方位守护就医体验的功能宝库
  • iOS 开发环境搭建完整指南 Xcode 安装配置、iOS 开发工具选择、ipa 打包与 App Store 上架实战经验
  • 【Node.js】Express 和 Koa 中间件的区别
  • 学习路之PHP--TP8+swoole
  • 【从零开始的大模型原理与实践教程】--第五章:动手搭建大模型LLaMA2
  • Vue.js 从入门到实践1:环境搭建、数据绑定与条件渲染
  • “潮涌之江,文兴浙里”文化推动高质量发展主题活动在西湖区调研
  • 【MongoDB】mongoDB数据迁移
  • 《C++多态入门:轻松理解虚函数与多态编程》
  • 虚拟化范式跃迁中的生命周期隐喻与命令哲学——解构Docker容器从抽象实体到可控资源的数字化生存法则
  • OpenLayers地图交互 -- 章节八:平移交互详解
  • AES+RSA 实现混合加密
  • 命名实体识别技术NER