设计模式之代理模式

引言

你坐在咖啡馆里,想点一杯招牌拿铁,但柜台前排着长队。这时,服务员走过来对你说:"交给我吧,您不用亲自排队------我帮您下单,顺便提醒您今天的甜点半价,还能自动避开您过敏的坚果配料。"这个看似贴心的服务员,其实就是一个"现实版代理":他替你完成核心任务(点单),同时悄然附加了额外服务(推荐、过滤风险)。

在软件设计中,代理模式(Proxy) 正是这种"中间人"思维的代码级演绎。它像一位精明的助手,挡在你和复杂对象之间:当你需要调用某个耗时的方法时,代理可以悄悄启动异步加载,避免界面卡顿;当你试图访问敏感数据时,代理会先检查权限,像图书馆管理员一样拦住越界请求;甚至当你调用远程服务时,代理会默默处理网络通信的细节,让你觉得就像在操作本地对象一样轻松。

你可能经常听过这样一句话:在程序员的世界里,没有什么延迟加载不能解决,如果有,就再加一层代理!是的,这就是我们今天要研究的代理模式。

概念

定义

代理模式是一种结构型设计模式,指为其他对象提供一种代理以控制对这个对象的访问。在代理模式中,代理对象充当了目标对象的替身,客户端对目标对象的访问实际上是通过代理对象来进行的。代理对象可以在客户端和目标对象之间增加额外的逻辑和功能,而客户端并不需要知道它访问的是代理对象而不是真正的目标对象。

结构

  • 抽象主题(Subject):定义了真实主题和代理主题的共同接口,这样在任何使用真实主题的地方都可以使用代理主题。
  • 真实主题(Real Subject):真正实现业务逻辑的对象,是代理对象所代表的真实对象,也就是最终要被访问的对象。
  • 代理主题(Proxy):代理对象,内部包含对真实主题的引用,实现了与真实主题相同的接口。它可以在调用真实主题的方法前后,添加一些额外的操作,如权限检查、缓存处理等。

工作原理

  • 客户端向代理对象发送请求,请求执行某个操作或获取某个资源。
  • 代理对象接收到请求后,会对请求进行处理。它可能会检查请求的合法性、权限等信息。
  • 如果请求满足条件,代理对象将请求转发给目标对象,让目标对象执行实际的操作。
  • 目标对象执行完操作后,将结果返回给代理对象。
  • 代理对象可能会对返回的结果进行进一步的处理,如缓存结果、对结果进行格式转换等,然后将最终结果返回给客户端。

设计原则

代理模式的设计原则主要围绕以下几个核心设计原则展开,这些原则帮助确保代理模式的实现既灵活又符合良好的软件设计规范:

1.单一职责原则

  • 定义:一个类应该只有一个引起它变化的原因,即一个类只负责一项职责。
  • 在代理模式中的应用:代理模式将职责分离,代理类负责控制对真实对象的访问,而真实对象只需关注其核心业务逻辑。例如,代理类可以处理日志记录、权限校验等非核心功能,而真实对象专注于业务实现

2.开闭原则

  • 定义:软件实体(类、模块、函数等)应该对扩展开放,对修改关闭。
  • 在代理模式中的应用:代理模式允许在不修改真实对象的情况下,通过扩展代理类来增加新的功能。例如,可以通过代理类添加缓存、日志记录等功能,而无需修改真实对象的代码

3.接口隔离原则

  • 定义:客户端不应该依赖它不需要的接口,即一个类对另一个类的依赖应该建立在最小的接口上。
  • 在代理模式中的应用:代理类和真实类通常实现相同的接口,确保客户端只需依赖该接口,而不需要了解具体的实现细节。这种设计使得代理类和真实类可以互换使用

4.依赖倒置原则

  • 定义:高层模块不应该依赖低层模块,两者都应该依赖抽象;抽象不应该依赖细节,细节应该依赖抽象。
  • 在代理模式中的应用:代理模式和真实类都依赖于抽象接口,而不是具体的实现。这种设计使得代理模式可以灵活地替换真实对象,而客户端代码无需修改

5.迪米特法则(最少知识原则)

  • 定义:一个对象应该对其他对象保持最少的了解,即只与直接的朋友通信。
  • 在代理模式中的应用:代理模式通过代理对象隐藏了真实对象的复杂性,客户端只需与代理对象交互,而不需要直接访问真实对象。这种设计减少了客户端与真实对象之间的耦合

6.里氏替换原则

  • 定义:子类应该能够替换父类并且不会影响程序的正确性。
  • 在代理模式中的应用:代理类和真实类都实现了相同的接口,因此代理类可以完全替换真实类,而客户端无需感知这种替换。这种设计确保了代理模式的透明性和可扩展性

分类

代理模式根据代理对象的创建时机和方式,可以分为以下几类:

  • 静态代理:代理类在程序运行前就已经存在,通常由程序员手动编写或通过工具生成。静态代理的优点是简单直观,但缺点是每个代理类只能代理一个特定的对象,扩展性较差
  • 动态代理:代理类在程序运行时动态生成,通常通过反射机制实现。动态代理的优点是灵活性高,可以根据需要生成不同的代理对象,减少代码量。
  • 强制代理:客户端必须通过代理对象访问真实对象,直接访问真实对象会被拒绝。这种模式通常用于控制对真实对象的访问权限。

下面我们使用引言中购买咖啡的例子编写三种代理的示例代码。

静态代理

静态代理是手动编写代理类,代理类和真实类实现相同的接口。

C++实现

C++ 复制代码
#include <iostream>
#include <string>

// 抽象主题:点单接口
class Order {
public:
    virtual void placeOrder(const std::string& item) = 0;
    virtual ~Order() = default;
};

// 真实主题:客户点单
class Customer : public Order {
public:
    void placeOrder(const std::string& item) override {
        std::cout << "顾客点单: " << item << std::endl;
    }
};

// 静态代理:服务员代理点单
class WaiterProxy : public Order {
private:
    Customer customer;
public:
    void placeOrder(const std::string& item) override {
        // 额外服务:推荐甜点
        std::cout << "服务员提醒: 今天的甜点半价哦!" << std::endl;
        // 过滤过敏配料
        if (item.find("坚果") != std::string::npos) {
            std::cout << "服务员提醒: 已避开坚果配料!" << std::endl;
        }
        // 核心任务:帮顾客点单
        customer.placeOrder(item);
    }
};

// 客户端代码
int main() {
    WaiterProxy waiter;
    waiter.placeOrder("招牌拿铁");
    waiter.placeOrder("坚果巧克力蛋糕");
    return 0;
}

Java实现

Java 复制代码
// 抽象主题:点单接口
interface Order {
    void placeOrder(String item);
}

// 真实主题:客户点单
class Customer implements Order {
    @Override
    public void placeOrder(String item) {
        System.out.println("顾客点单: " + item);
    }
}

// 静态代理:服务员代理点单
class WaiterProxy implements Order {
    private final Customer customer;

    public WaiterProxy(Customer customer) {
        this.customer = customer;
    }

    @Override
    public void placeOrder(String item) {
        // 额外服务:推荐甜点
        System.out.println("服务员提醒: 今天的甜点半价哦!");
        // 过滤过敏配料
        if (item.contains("坚果")) {
            System.out.println("服务员提醒: 已避开坚果配料!");
        }
        // 核心任务:帮顾客点单
        customer.placeOrder(item);
    }
}

// 客户端代码
public class StaticProxyDemo {
    public static void main(String[] args) {
        Customer customer = new Customer();
        WaiterProxy waiter = new WaiterProxy(customer);

        waiter.placeOrder("招牌拿铁");
        waiter.placeOrder("坚果巧克力蛋糕");
    }
}

动态代理

C++实现

C++ 本身不支持动态代理(如 Java 的反射机制),但可以通过函数指针或std::function模拟动态代理。

C++ 复制代码
#include <iostream>
#include <string>
#include <functional>

// 抽象主题:点单接口
class Order {
public:
    virtual void placeOrder(const std::string& item) = 0;
    virtual ~Order() = default;
};

// 真实主题:客户点单
class Customer : public Order {
public:
    void placeOrder(const std::string& item) override {
        std::cout << "顾客点单: " << item << std::endl;
    }
};

// 动态代理:服务员代理点单
class DynamicProxy {
private:
    std::function<void(const std::string&)> realTask;
public:
    DynamicProxy(std::function<void(const std::string&)> task) : realTask(task) {}

    void placeOrder(const std::string& item) {
        // 额外服务:推荐甜点
        std::cout << "服务员提醒: 今天的甜点半价哦!" << std::endl;
        // 过滤过敏配料
        if (item.find("坚果") != std::string::npos) {
            std::cout << "服务员提醒: 已避开坚果配料!" << std::endl;
        }
        // 核心任务:帮顾客点单
        realTask(item);
    }
};

// 客户端代码
int main() {
    Customer customer;
    DynamicProxy waiter([&](const std::string& item) {
        customer.placeOrder(item);
    });

    waiter.placeOrder("招牌拿铁");
    waiter.placeOrder("坚果巧克力蛋糕");
    return 0;
}

Java实现

Java 支持动态代理,通过java.lang.reflect.Proxy实现。

Java 复制代码
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

// 抽象主题:点单接口
interface Order {
    void placeOrder(String item);
}

// 真实主题:客户点单
class Customer implements Order {
    @Override
    public void placeOrder(String item) {
        System.out.println("顾客点单: " + item);
    }
}

// 动态代理:服务员代理点单
class WaiterProxyHandler implements InvocationHandler {
    private final Object target;

    public WaiterProxyHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 额外服务:推荐甜点
        System.out.println("服务员提醒: 今天的甜点半价哦!");
        // 过滤过敏配料
        if (args[0].toString().contains("坚果")) {
            System.out.println("服务员提醒: 已避开坚果配料!");
        }
        // 核心任务:帮顾客点单
        return method.invoke(target, args);
    }
}

// 客户端代码
public class DynamicProxyDemo {
    public static void main(String[] args) {
        Customer customer = new Customer();
        Order waiter = (Order) Proxy.newProxyInstance(
                Customer.class.getClassLoader(),
                new Class[]{Order.class},
                new WaiterProxyHandler(customer)
        );

        waiter.placeOrder("招牌拿铁");
        waiter.placeOrder("坚果巧克力蛋糕");
    }
}

强制代理

强制代理要求客户端必须通过代理访问真实对象,直接访问真实对象会被拒绝。

C++实现

C++ 复制代码
#include <iostream>
#include <string>
#include <memory>

// 抽象主题:点单接口
class Order {
public:
    virtual void placeOrder(const std::string& item) = 0;
    virtual void setProxy(std::shared_ptr<Order> proxy) = 0;
    virtual ~Order() = default;
};

// 真实主题:客户点单
class Customer : public Order {
private:
    std::shared_ptr<Order> proxy;
public:
    void placeOrder(const std::string& item) override {
        if (proxy.get() != this) {
            std::cout << "拒绝直接点单,请通过服务员代理!" << std::endl;
            return;
        }
        std::cout << "顾客点单: " << item << std::endl;
    }

    void setProxy(std::shared_ptr<Order> proxy) override {
        this->proxy = proxy;
    }
};

// 强制代理:服务员代理点单
class WaiterProxy : public Order, public std::enable_shared_from_this<WaiterProxy> {
private:
    std::shared_ptr<Customer> customer;
public:
    WaiterProxy(std::shared_ptr<Customer> customer) : customer(customer) {
        customer->setProxy(shared_from_this());
    }

    void placeOrder(const std::string& item) override {
        // 额外服务:推荐甜点
        std::cout << "服务员提醒: 今天的甜点半价哦!" << std::endl;
        // 过滤过敏配料
        if (item.find("坚果") != std::string::npos) {
            std::cout << "服务员提醒: 已避开坚果配料!" << std::endl;
        }
        // 核心任务:帮顾客点单
        customer->placeOrder(item);
    }

    void setProxy(std::shared_ptr<Order> proxy) override {
        // 强制代理不需要设置
    }
};

// 客户端代码
int main() {
    std::shared_ptr<Customer> customer = std::make_shared<Customer>();
    std::shared_ptr<WaiterProxy> waiter = std::make_shared<WaiterProxy>(customer);

    // 直接点单会被拒绝
    customer->placeOrder("招牌拿铁");

    // 通过代理点单
    waiter->placeOrder("招牌拿铁");
    waiter->placeOrder("坚果巧克力蛋糕");
    return 0;
}

Java实现

Java 复制代码
// 抽象主题:点单接口
interface Order {
    void placeOrder(String item);
    void setProxy(Order proxy);
}

// 真实主题:客户点单
class Customer implements Order {
    private Order proxy;

    @Override
    public void placeOrder(String item) {
        if (proxy == null) {
            System.out.println("拒绝直接点单,请通过服务员代理!");
            return;
        }
        System.out.println("顾客点单: " + item);
    }

    @Override
    public void setProxy(Order proxy) {
        this.proxy = proxy;
    }
}

// 强制代理:服务员代理点单
class WaiterProxy implements Order {
    private final Customer customer;

    public WaiterProxy(Customer customer) {
        this.customer = customer;
        this.customer.setProxy(this);
    }

    @Override
    public void placeOrder(String item) {
        // 额外服务:推荐甜点
        System.out.println("服务员提醒: 今天的甜点半价哦!");
        // 过滤过敏配料
        if (item.contains("坚果")) {
            System.out.println("服务员提醒: 已避开坚果配料!");
        }
        // 核心任务:帮顾客点单
        customer.placeOrder(item);
    }

    @Override
    public void setProxy(Order proxy) {
        // 强制代理不需要设置
    }
}

// 客户端代码
public class ForceProxyDemo {
    public static void main(String[] args) {
        Customer customer = new Customer();
        WaiterProxy waiter = new WaiterProxy(customer);

        // 直接点单会被拒绝
        customer.placeOrder("招牌拿铁");

        // 通过代理点单
        waiter.placeOrder("招牌拿铁");
        waiter.placeOrder("坚果巧克力蛋糕");
    }
}

代理模式与外观模式的差异

比较维度 代理模式 外观模式
定义 为其他对象提供一种代理以控制对这个对象的访问。客户端通过代理对象间接访问目标对象,代理对象可以在访问前后进行额外操作。 为子系统中的一组接口提供一个一致的界面,定义了一个高层接口,这个接口使得这一子系统更加容易使用。
相同点 都属于结构型设计模式,主要关注如何将类或对象组合成更大的结构。 都在一定程度上起到了隔离的作用,隐藏了某些实现细节,让客户端与具体实现之间的耦合度降低。
不同点 目的 :重点在于控制对对象的访问,可实现权限控制、延迟加载、缓存等功能。 对象数量 :通常涉及一个目标对象和一个代理对象,代理对象和目标对象实现相同的接口,客户端使用代理对象就像使用目标对象一样。 使用场景 :适用于需要对对象的访问进行控制、增强功能或优化性能的场景,如远程代理、虚拟代理、保护代理等。 结构层次:代理对象和目标对象处于同一层次,是一种一对一的关系。 目的 :主要是简化复杂子系统的使用,为子系统提供一个统一的、简单的接口,方便客户端调用。 对象数量 :涉及多个子系统对象,外观对象封装了多个子系统的功能,客户端只需要和外观对象交互。 使用场景 :当一个复杂系统有多个子系统,客户端需要与这些子系统交互时,使用外观模式可以降低客户端的使用难度。 结构层次:外观对象处于更高层次,它对多个子系统进行封装,是一种一对多的关系。

代理模式的优缺点

优点

1. 访问控制与安全性增强

  • 权限管理:通过代理模式可以在访问对象时进行权限检查,只有满足特定条件的客户端才能访问目标对象。例如,在一个企业级系统中,某些敏感数据的访问需要特定的用户权限,代理对象可以在调用目标对象的方法前检查用户权限,若权限不足则阻止访问,从而增强系统的安全性。
  • 保护隐私:代理可以隐藏目标对象的具体实现细节,只向客户端暴露必要的接口。比如在一些网络服务中,代理服务器可以隐藏真实服务器的 IP 地址等信息,防止客户端直接访问服务器,减少安全风险。

2. 增强系统性能

  • 延迟加载(虚拟代理):对于一些创建开销较大或占用资源较多的对象,代理模式可以实现延迟加载。在真正需要使用对象时才进行创建和初始化,避免不必要的资源浪费。例如,在图形处理软件中,当打开一个包含大量图片的文档时,使用虚拟代理可以先显示图片的占位符,等用户真正需要查看某张图片时再加载它,提高软件的响应速度。
  • 缓存机制:代理对象可以实现缓存功能,对于一些频繁访问的结果,代理可以将其缓存起来,下次再收到相同请求时,直接从缓存中返回结果,而不需要再次调用目标对象的方法,减少了系统的开销。比如在 Web 应用中,代理服务器可以缓存一些静态页面或数据,提高网站的访问速度。

3. 提高代码可维护性和可扩展性

  • 职责分离:代理模式将与对象访问相关的操作(如权限检查、缓存处理等)从目标对象中分离出来,使得目标对象可以专注于自身的核心业务逻辑,代码结构更加清晰,便于维护和修改。例如,在一个电商系统中,商品信息的获取可能需要进行缓存和权限检查,使用代理模式可以将这些操作封装在代理对象中,商品服务类只负责处理商品信息的核心业务。
  • 功能扩展:可以在不修改目标对象代码的情况下,通过代理对象为目标对象添加新的功能。例如,在一个日志记录系统中,若需要对某个业务对象的方法调用进行日志记录,可以创建一个代理对象,在代理对象的方法中添加日志记录的逻辑,而不需要修改业务对象的代码。

4. 远程访问支持(远程代理)

  • 屏蔽网络细节:在分布式系统中,远程代理可以让客户端像访问本地对象一样访问远程对象,隐藏了网络通信的细节,使得客户端代码的编写更加简单。例如,在一个跨地区的企业级应用中,客户端可以通过远程代理调用远程服务器上的服务,而不需要关心网络连接、数据传输等底层细节。

缺点

1. 增加系统复杂性

  • 代码量增加:引入代理模式需要创建额外的代理类,这会增加代码的数量和复杂度。特别是在实现复杂的代理逻辑时,代理类的代码可能会变得冗长和难以理解。例如,一个具有多种权限检查和缓存策略的代理类,其代码可能包含大量的条件判断和数据处理逻辑。
  • 理解难度加大:对于不熟悉代理模式的开发者来说,理解代理对象和目标对象之间的关系以及代理类的工作原理可能会有一定的困难,增加了代码的学习成本和维护难度。

2. 可能带来性能开销

  • 额外的调用开销:由于客户端需要通过代理对象来访问目标对象,会增加一次额外的调用过程,可能会导致一定的性能损失。特别是在对性能要求极高的系统中,这种额外的调用开销可能会变得比较明显。
  • 内存开销:代理对象本身也需要占用一定的内存空间,当系统中存在大量的代理对象时,会增加系统的内存开销。

3. 可能导致调试困难

  • 调用链变长:使用代理模式会使调用链变长,当出现问题时,定位问题的难度会增加。因为错误可能出现在代理对象、目标对象或者它们之间的交互过程中,需要仔细排查才能找到问题的根源。
  • 代理逻辑干扰:代理对象中可能包含复杂的逻辑,这些逻辑可能会干扰对目标对象本身的调试。例如,代理对象的缓存逻辑可能会导致某些情况下目标对象的方法没有被真正调用,从而影响调试的准确性。

注意事项

设计层面

1. 接口设计

  • 一致性:代理对象和目标对象要实现相同的接口(在基于接口的代理模式中),确保客户端可以无缝地使用代理对象替代目标对象。若接口不一致,会破坏代理模式的透明性,增加客户端使用的复杂度。
  • 粒度:接口的设计要合理,既不能过于宽泛包含过多无关的方法,也不能过于狭窄导致功能缺失。例如,若设计一个文件操作代理,接口应只包含与文件操作相关的核心方法,避免混入网络操作等无关功能。

2. 代理类型选择

  • 根据需求匹配:不同类型的代理(如远程代理、虚拟代理、保护代理、智能引用代理)适用于不同的场景。要根据具体的业务需求选择合适的代理类型。例如,若要控制对敏感资源的访问,应选择保护代理;若要优化大对象的加载,可选择虚拟代理。
  • 避免过度使用:不要为了使用代理模式而强行使用,若没有实际的控制访问、增强功能等需求,引入代理会增加系统的复杂度。

3. 代理链设计

  • 避免过长:如果使用多个代理形成代理链,要避免代理链过长。过长的代理链会增加系统的复杂度和性能开销,同时也会让调试和维护变得困难。例如,在一个复杂的电商系统中,若对商品信息的获取设置了过多的代理进行权限检查、缓存处理等,会导致请求处理时间变长。
  • 清晰的职责划分:每个代理在代理链中要有明确的职责,避免职责重叠或模糊。例如,一个代理负责权限检查,另一个代理负责日志记录,各自做好自己的工作。

性能层面

1.性能开销

  • 额外调用开销:代理模式会引入额外的调用,因为客户端是通过代理对象间接访问目标对象,这可能会带来一定的性能损失。在对性能要求极高的系统中,要谨慎使用代理模式。例如,在高频交易系统中,过多的代理调用可能会影响交易的响应速度。
  • 内存开销:代理对象本身也会占用一定的内存空间,尤其是在创建大量代理对象时,会增加系统的内存负担。要注意对代理对象的生命周期进行管理,及时释放不再使用的代理对象。

2. 缓存策略

  • 有效性:如果代理使用了缓存机制,要确保缓存的有效性。例如,当目标对象的数据发生变化时,要及时更新缓存,否则会导致客户端获取到过时的数据。
  • 缓存大小:合理设置缓存的大小,避免缓存占用过多的内存资源。可以采用一些缓存淘汰算法(如 LRU 算法)来管理缓存。

维护层面

1. 代码可读性和可维护性

  • 注释和文档:由于代理模式会增加代码的复杂度,要为代理类和相关代码添加详细的注释和文档,解释代理的功能、工作原理以及与目标对象的关系,方便后续开发者理解和维护。
  • 模块化设计:将代理逻辑进行模块化设计,每个代理类只负责单一的功能,降低代码的耦合度,便于修改和扩展。例如,将权限检查、日志记录等功能分别封装在不同的代理类中。

2. 版本兼容性

  • 接口变更:当目标对象的接口发生变更时,要确保代理对象也能相应地进行更新,以保证代理模式的正常工作。可以采用一些设计原则(如开闭原则)来减少接口变更对代理类的影响。
  • 依赖管理:注意代理类与其他模块的依赖关系,避免因依赖的库或模块升级而导致代理类出现问题。

安全层面

1. 权限控制

  • 完整性:在使用保护代理进行权限控制时,要确保权限检查的完整性。避免出现权限绕过的漏洞,防止非法用户访问敏感资源。
  • 动态更新:当用户的权限发生变化时,要及时更新代理的权限检查逻辑,保证系统的安全性。

2. 数据安全

  • 数据传输:在远程代理中,要注意数据传输的安全性。对传输的数据进行加密处理,防止数据在传输过程中被窃取或篡改。
  • 数据访问:代理对象在处理数据时,要遵循数据安全的原则,避免将敏感数据泄露给未经授权的客户端。

应用场景

网络通信领域

  • 远程代理
  • 分布式系统:在分布式系统中,不同的服务可能部署在不同的服务器上。通过远程代理,客户端可以像调用本地对象的方法一样调用远程服务,隐藏了网络通信的细节。例如,在微服务架构中,一个服务需要调用另一个服务的功能,使用远程代理可以简化服务之间的调用过程,让开发者无需关心网络连接、数据传输等底层操作。
  • 云计算:在云计算环境中,用户通过云服务提供商提供的代理接口来访问云端的资源,如存储服务、计算服务等。代理负责处理与云端服务器的通信,用户只需关注业务逻辑,无需了解云计算基础设施的具体实现。
  • 防火墙代理
  • 企业网络安全:企业内部网络通常会设置防火墙和代理服务器,以控制员工对外部网络的访问。代理服务器作为企业内部网络和外部网络之间的中间层,对员工的网络请求进行过滤和监控,只允许符合安全策略的请求通过,从而保护企业内部网络免受外部攻击和恶意软件的侵害。

资源管理与性能优化

  • 虚拟代理
  • 图形和多媒体处理:在图形界面应用程序中,加载大尺寸的图片或视频文件可能会导致界面卡顿。使用虚拟代理可以在图片或视频还未完全加载时,先显示一个占位符或缩略图,当用户真正需要查看完整内容时,再加载实际的文件,提高用户体验。例如,在浏览器中浏览网页时,图片可能会以模糊的缩略图形式快速显示,待完全加载后再清晰显示。
  • 数据库查询:对于一些复杂的数据库查询,可能需要较长的时间才能返回结果。虚拟代理可以在查询结果还未返回时,先返回一个临时对象,当客户端需要访问具体数据时,再执行实际的数据库查询操作,避免长时间的等待。
  • 缓存代理
  • Web 应用:在 Web 应用中,为了减轻服务器的负载和提高响应速度,可以使用缓存代理。代理服务器将经常访问的网页或数据缓存起来,当有新的请求时,先检查缓存中是否存在相应的内容,如果存在则直接返回缓存结果,无需再次访问服务器,减少了服务器的压力和用户的等待时间。
  • 内容分发网络(CDN):CDN 是一种广泛应用的缓存代理技术,它将网站的静态资源(如图片、脚本、样式表等)缓存到分布在全球各地的节点服务器上。当用户访问网站时,CDN 会根据用户的地理位置选择最近的节点服务器提供资源,大大提高了资源的加载速度。

访问控制与安全

  • 保护代理
  • 企业信息系统:在企业的信息系统中,不同的用户角色具有不同的权限。保护代理可以在用户访问系统资源时进行权限检查,只有具有相应权限的用户才能访问特定的资源。例如,在一个财务管理系统中,普通员工只能查看自己的报销记录,而财务管理人员可以进行财务报表的生成和审批等操作,保护代理会根据用户的角色和权限来控制对不同功能模块的访问。
  • 文件系统:操作系统中的文件系统可以使用保护代理来控制用户对文件和文件夹的访问权限。代理会检查用户的身份和权限,只有经过授权的用户才能进行读取、写入或修改文件的操作,确保文件系统的安全性。

日志记录与监控

  • 日志代理
  • 系统监控:在大型软件系统中,为了方便对系统的运行状态进行监控和分析,可以使用日志代理。代理对象在调用目标对象的方法前后记录相关的日志信息,如方法的调用时间、输入参数、返回结果等。这些日志信息可以用于故障排查、性能分析和安全审计等。例如,在一个电商系统中,日志代理可以记录用户的下单、支付等操作信息,帮助企业分析用户行为和系统性能。
  • 性能监控:通过代理模式可以对系统中关键对象的方法调用进行监控,统计方法的执行时间、调用次数等性能指标。代理对象可以在方法调用前后记录时间戳,计算方法的执行时间,并将这些性能数据发送到监控系统进行分析,帮助开发人员及时发现系统中的性能瓶颈。
相关推荐
瑞金彭于晏10 分钟前
通俗易懂版 Maven 科普,maven是什么?
java·maven
好看资源平台14 分钟前
Java Web开发实战与项目——Spring Boot与Spring Cloud微服务项目实战
java
.猫的树20 分钟前
Java集合List快速实现重复判断的10种方法深度解析
java·开发语言·list·集合
星霜旅人21 分钟前
【C++】深入理解List:双向链表的应用
c++
刀客12325 分钟前
C++ STL(三)list
开发语言·c++
littlegirll29 分钟前
命令行方式安装KFS同步KES到KADB
java·大数据·数据库
-拟墨画扇-34 分钟前
C++ | 面向对象 | 类
c++·深拷贝··静态成员·友元函数·类拷贝构造函数·类构造析构函数
阿巴~阿巴~34 分钟前
关于回溯算法中的剪枝是否需要for循环的总结归纳
数据结构·c++·算法·深度优先·剪枝
itachi-uchiha35 分钟前
深入理解 Linux 中的 last 和 lastb 命令
java·linux·服务器
xiaoyustudiowww43 分钟前
JSP + Servlet 实现 AJAX(纯JS版)
java·javascript·servlet