介绍

代理模式是一种结构型设计模式, 让你能够提供对象的替代品或其占位符(只是代理,所以只能代替它占位置)。 代理控制着对于原对象的访问, 并允许在将请求提交给对象前后进行一些处理。

开发时遇到的问题

当有一个消耗大量系统资源的巨型对象, 你只是偶尔需要使用它, 并非总是需要。

假如初始时就创建 : 那太消耗资源了,我可能一次也不会使用.

假如我想延迟创建或者加入一些拓展处理 :太麻烦了,每个地方的创建代码都要添加这些逻辑.(后面会提到)

还有一些通常问题:隐藏细节,权限认证(加判断).

解决方案

我使用一个代理模式代理这个巨型对象的创建, 你可以在程序编译的时候就创建这个代理, (它很小). 并且在你真正想使用巨型对象的时候可以调用代理的一个创建接口 , 去真正创建一个巨型对象 . 这就是延迟创建

它也相当于是一个中间类, 你可以添加任意的一些拓展 去给创建巨型对象时增加更多细节. (在类的主要业务逻辑前后执行一些工作)

UML 图

复杂方式 : 代理和服务是服务接口的实现. ( 服务接口是抽象类 这样比较复杂但可以根据情况选择是否使用代理)

简单方式 : 直接调用代理. 但这种方式没有统一接口不利于扩展,适合一个代理代理一个服务的情况


经过我的查找 复杂方式主要有以下几个好处.

  • 统一接口: 通过抽象类(或接口),所有代理类都遵循相同的接口,使得代码更加一致。你可以使用多态来处理不同类型的代理,代码的使用者不需要关心代理的具体类型,而只需要关心它们是否遵循接口。
  • 代码复用: 如果有多个代理类之间有相同的逻辑(例如,都需要进行前置处理和后置处理),那么这些相同的行为可以放到抽象类或者基类中,从而避免重复代码,减少冗余。不同的代理类可以在继承基础上重用相同的行为。
  • 扩展性: 由于所有的代理类都遵循相同的接口或抽象类,在需要扩展系统时,你只需要增加一个新的代理类,并且它会与现有代码兼容。只要实现了抽象类中的方法,新的代理类就能顺利加入系统。
  • 易于替换和维护: 通过统一的接口,代理类可以轻松地进行替换。在需要修改代理的行为时,只需要修改代理类的实现,不需要修改客户端代码或其他代理类。

实现代码

我拓展了一下代理类型 有两个了 . 虽然不使用抽象类也能完成这种操作, 但又很多缺点. (上述好处的反面)

#include <iostream>

// 抽象类
class Subject
{
public:
    virtual void request() = 0; // 纯虚函数
    virtual ~Subject() = default;
};

class RealSubject : public Subject
{
public:
    void request() override
    {
        std::cout << "RealSubject's request" << std::endl;
    }
};

class Proxy : public Subject
{
private:
    RealSubject *realSubject; // 代理持有真实对象的引用

public:
    Proxy() : realSubject(new RealSubject()) {}

    ~Proxy()
    {
        delete realSubject;
    }

    void request() override
    {
        std::cout << "Proxy: Pre-processing" << std::endl;
        realSubject->request(); // 调用 RealSubject 的方法
        std::cout << "Proxy: Post-processing" << std::endl;
    }
};
class AnotherSubject : public Subject
{
public:
    void request() override
    {
        std::cout << "AnotherSubject's request" << std::endl;
    }
};

class AnotherProxy : public Subject
{
private:
    AnotherSubject *anotherSubject;

public:
    AnotherProxy() : anotherSubject(new AnotherSubject()) {}

    ~AnotherProxy()
    {
        delete anotherSubject;
    }

    void request() override
    {
        std::cout << "AnotherProxy: Pre-processing" << std::endl;
        anotherSubject->request(); // 调用 AnotherSubject 的方法
        std::cout << "AnotherProxy: Post-processing" << std::endl;
    }
};

int main()
{
    Subject *proxy1 = new Proxy(); // 代理 RealSubject
    proxy1->request();

    std::cout << "---------" << std::endl;

    Subject *proxy2 = new AnotherProxy(); // 代理 AnotherSubject
    proxy2->request();

    delete proxy1;
    delete proxy2;

    return 0;
}

应用场景

  1. 远程代理:当需要访问远程对象时,可以使用代理模式来隐藏底层的网络通信细节,客户端通过代理对象访问远程对象。
  2. 虚拟代理:当创建一个对象的成本很高时,可以使用代理模式来延迟对象的实际创建,只有在真正需要时才创建真实对象。
  3. 安全代理:当需要对对象的访问进行控制时,可以使用代理模式来添加额外的安全性检查,例如权限验证等。
  4. 缓存代理:当需要缓存对象的结果以提高性能时,可以使用代理模式来实现缓存功能,代理对象可以在调用真实对象之前检查缓存,并返回缓存结果。
  5. 日志记录代理:当需要记录对象的操作日志时,可以使用代理模式来在调用真实对象之前或之后添加日志记录的功能。
  6. 智能引用 : 代理会将所有获取了指向服务对象或其结果的客户端记录在案。 代理会时不时地遍历各个客户端, 检查它们是否仍在运行。 如果相应的客户端列表为空, 代理就会销毁该服务对象, 释放底层系统资源。代理还可以记录客户端是否修改了服务对象。 其他客户端还可以复用未修改的对象。

与其他模式的关系

  • 适配器模式能为被封装对象提供不同的接口, 代理模式能为对象提供相同的接口, 装饰模式则能为对象提供加强的接口。
  • 外观模式与代理的相似之处在于它们都缓存了一个复杂实体并自行对其进行初始化。 代理与其服务对象遵循同一接口, 使得自己和服务对象可以互换, 在这一点上它与外观不同。
  • 装饰和代理有着相似的结构, 但是其意图却非常不同。 这两个模式的构建都基于组合原则, 也就是说一个对象应该将部分工作委派给另一个对象。 两者之间的不同之处在于代理通常自行管理其服务对象的生命周期, 而装饰的生成则总是由客户端进行控制。

优缺点

优点

缺点

可以在客户端毫无察觉的情况下控制服务对象。

代码可能会变得复杂,因为需要新建许多类。

如果客户端对服务对象的生命周期没有特殊要求,可以对生命周期进行管理。

服务响应可能会延迟。

即使服务对象还未准备好或不存在,代理也可以正常工作。

开闭原则。可以在不对服务或客户端做出修改的情况下创建新代理。

借鉴连接 菜鸡作者 语句有问题的地方 欢迎指正!

更多推荐