设计模式 - 代理模式

代理模式(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. 延迟请求:某些代理可能会引入请求的延迟,例如远程代理需要通过网络进行通信,而虚拟代理需要延迟真实对象的创建。
相关推荐
菜菜-plus1 小时前
java 设计模式 模板方法模式
java·设计模式·模板方法模式
萨达大1 小时前
23种设计模式-模板方法(Template Method)设计模式
java·c++·设计模式·软考·模板方法模式·软件设计师·行为型设计模式
机器视觉知识推荐、就业指导3 小时前
C++设计模式:原型模式(Prototype)
c++·设计模式·原型模式
阳光开朗_大男孩儿3 小时前
组合模式和适配器模式的区别
设计模式·组合模式·适配器模式
MinBadGuy3 小时前
【GeekBand】C++设计模式笔记13_Flyweight_享元模式
c++·设计模式
Clang's Blog5 小时前
23种设计模式详解(以Java为例)
java·开发语言·设计模式
程序员奇奥5 小时前
设计模式——简单工厂模型、工厂模式、抽象工厂模式、单例模式、代理模式、模板模式
单例模式·设计模式·抽象工厂模式
hxj..5 小时前
【设计模式】代理模式
java·设计模式·代理模式·动态代理
十五年专注C++开发6 小时前
C++不完整类型(Incomplete Type)的检测与避免
开发语言·c++·算法·设计模式
Theodore_102215 小时前
7 设计模式原则之合成复用原则
java·开发语言·jvm·设计模式·java-ee·合成复用原则