设计模式 - 代理模式

代理模式(Proxy Pattern)是一种结构型设计模式,广泛应用于远程调用、访问控制、缓存优化等场景。

它通过为对象提供一个代理来控制对这个对象的访问。代理对象在客户端与目标对象之间起到一个中介的作用,客户端不直接与实际对象交互,而是通过代理对象来进行调用。代理对象可以在目标对象被调用之前或之后,添加额外的功能。代理模式通过这种方式来增强对象的灵活性,同时又不会对原有对象进行修改。这种设计思想非常符合开闭原则(Open/Closed Principle),有助于代码的扩展和维护。


代理模式的分类
  1. 远程代理(Remote Proxy):为位于不同地址空间的对象提供代理,通常是用于客户端与远程服务器之间的通信。通过代理对象隐藏实际网络连接的细节,使得客户端以为是在调用本地对象。
  2. 虚拟代理(Virtual Proxy):用于创建开销较大的对象。当对象的创建很耗资源且不一定需要时,虚拟代理在真正需要的时候才创建真实对象,并进行延迟加载。
  3. 保护代理(Protection Proxy):控制对目标对象的访问权限,通常用于限制一些特定用户或操作。
  4. 智能代理(Smart Proxy):为目标对象增加一些额外的功能,例如计数器、日志记录等。

代理模式的结构

代理模式通常包含以下几个角色:

  • 抽象主题(Subject):定义代理类和真实对象类的接口,使得代理对象和真实对象可以互换。
  • 真实主题(RealSubject):实现抽象主题,定义了代理对象所代表的真实对象。
  • 代理主题(Proxy):实现抽象主题,并且持有一个真实主题对象的引用。代理主题可以在调用真实对象之前或者之后进行一些额外的处理。

类图
plaintext 复制代码
   Client
     |
     v
+-------------+        +-------------+
|  Subject    |<-------|  Proxy      |
+-------------+        +-------------+
      ^                      |
      |                      v
+-------------+       +-------------+
| RealSubject |       | (Hold Reference to)
+-------------+       | RealSubject   |
                      +-------------+

Java代码示例

以银行账户访问为例:

java 复制代码
// Step 1: 创建一个接口
interface BankAccount {
    void deposit(double amount);
    void withdraw(double amount);
    double getBalance();
}

// Step 2: 实现真实对象类
class RealBankAccount implements BankAccount {
    private double balance;

    public RealBankAccount(double balance) {
        this.balance = balance;
    }

    @Override
    public void deposit(double amount) {
        balance += amount;
        System.out.println("Deposited " + amount + ", Current Balance: " + balance);
    }

    @Override
    public void withdraw(double amount) {
        if (amount <= balance) {
            balance -= amount;
            System.out.println("Withdrew " + amount + ", Current Balance: " + balance);
        } else {
            System.out.println("Insufficient funds");
        }
    }

    @Override
    public double getBalance() {
        return balance;
    }
}

// Step 3: 创建代理对象类
class BankAccountProxy implements BankAccount {
    private RealBankAccount realBankAccount;
    private String owner;

    public BankAccountProxy(String owner, double initialBalance) {
        this.owner = owner;
        this.realBankAccount = new RealBankAccount(initialBalance);
    }

    @Override
    public void deposit(double amount) {
        System.out.println("Proxy: Deposit access granted for " + owner);
        realBankAccount.deposit(amount);
    }

    @Override
    public void withdraw(double amount) {
        System.out.println("Proxy: Withdraw access granted for " + owner);
        realBankAccount.withdraw(amount);
    }

    @Override
    public double getBalance() {
        System.out.println("Proxy: Balance access granted for " + owner);
        return realBankAccount.getBalance();
    }
}

// Step 4: 客户端代码
public class ProxyPatternExample {
    public static void main(String[] args) {
        BankAccount account = new BankAccountProxy("John", 1000);
        account.deposit(500);
        account.withdraw(300);
        System.out.println("Final Balance: " + account.getBalance());
    }
}
  • BankAccount:定义了账户的操作,作为抽象主题。
  • RealBankAccount:为真实的银行账户类,实现了具体的账户操作。
  • BankAccountProxy:代理类,实现了对真实银行账户的代理,在每次操作之前进行一些信息打印(可以是权限校验或者日志记录等)。
  • 客户端只与代理类 BankAccountProxy 交互,而代理类最终会调用真实的 RealBankAccount

现实中的应用
1. 动态代理在 RPC(远程过程调用)中的应用

RPC 机制可以让客户端调用远程服务就像调用本地服务一样。RPC 系统通常会为每个远程服务提供一个代理对象,代理对象负责将调用请求转发到远程服务器上,然后将返回结果传回客户端。这样,客户端代码不需要关注底层的网络通信逻辑,只需像调用本地对象一样使用远程服务。

2. 虚拟代理在图片加载中的应用

在图形用户界面(GUI)开发中,加载大型图片文件可能会非常耗时。因此,可以使用虚拟代理来代替图片对象,在图片真正被加载之前显示一个占位符。当图片加载完成后,代理对象才会将请求委托给真实的图片对象。这种模式常见于网页浏览器、图片查看器中,以提高用户体验。

3. 保护代理在权限控制中的应用

在大型企业级应用中,保护代理可以用于控制对敏感数据的访问。例如,某些用户可能只能查看某些报表而不能修改数据。代理类可以在请求到达真实对象之前,进行权限检查,确保只有授权用户才能进行操作。这在企业的权限管理、系统安全等方面非常有用。

4. 缓存代理在浏览器缓存中的应用

代理模式在缓存中也是一个很典型的应用。例如,在浏览器中,当用户请求某个页面时,浏览器可能会先检查代理对象是否有缓存的页面副本,如果有就直接返回,从而减少对服务器的访问。这种方式可以提高系统性能,减少网络请求带来的延迟。


代理模式的优点和缺点
优点:
  1. 控制访问:代理模式可以控制对目标对象的访问,例如在保护代理中,可以实现权限管理。
  2. 增加功能:可以在不修改目标对象的前提下,通过代理对象增加额外的功能,例如日志、计数器、延迟加载等。
  3. 降低系统耦合:客户端只和代理类打交道,代理类可以屏蔽底层的复杂逻辑和实现细节。
缺点:
  1. 增加系统复杂性:代理模式增加了一个新的代理类,使系统更加复杂。
  2. 延迟请求:某些代理可能会引入请求的延迟,例如远程代理需要通过网络进行通信,而虚拟代理需要延迟真实对象的创建。
相关推荐
vker2 小时前
第 1 天:单例模式(Singleton Pattern)—— 创建型模式
java·设计模式
晨米酱20 小时前
JavaScript 中"对象即函数"设计模式
前端·设计模式
数据智能老司机1 天前
精通 Python 设计模式——分布式系统模式
python·设计模式·架构
数据智能老司机1 天前
精通 Python 设计模式——并发与异步模式
python·设计模式·编程语言
数据智能老司机1 天前
精通 Python 设计模式——测试模式
python·设计模式·架构
数据智能老司机1 天前
精通 Python 设计模式——性能模式
python·设计模式·架构
使一颗心免于哀伤1 天前
《设计模式之禅》笔记摘录 - 21.状态模式
笔记·设计模式
数据智能老司机2 天前
精通 Python 设计模式——创建型设计模式
python·设计模式·架构
数据智能老司机2 天前
精通 Python 设计模式——SOLID 原则
python·设计模式·架构
烛阴2 天前
【TS 设计模式完全指南】懒加载、缓存与权限控制:代理模式在 TypeScript 中的三大妙用
javascript·设计模式·typescript