Bootstrap

Java设计模式 十三 代理模式 (Proxy Pattern)

代理模式 (Proxy Pattern)

代理模式是一种结构型设计模式,它为其他对象提供一种代理(或占位符)以控制对该对象的访问。通过代理模式,我们可以在不修改目标对象的情况下,控制对其的访问,添加额外的功能,比如懒加载、权限检查、日志记录等。

代理模式主要通过为目标对象创建一个代理对象来代替真实对象,代理对象可以控制对目标对象的访问,并可以在访问时提供附加功能。


1. 代理模式的组成

代理模式通常包括以下角色:

  • Subject(主题角色): 定义了真实对象和代理对象的公共接口。真实对象和代理对象都需要实现这个接口。
  • RealSubject(真实主题角色): 真实对象,定义了实际的业务操作逻辑。客户端可以通过代理对象来访问真实主题。
  • Proxy(代理角色): 代理对象,控制对真实对象的访问。代理对象可能会执行一些额外的操作(如权限检查、日志记录等),然后再委托给真实对象处理。
  • Client(客户端): 客户端通过代理对象来访问目标对象,而不是直接访问真实对象。

2. 代理模式的优点

  1. 透明化访问: 客户端无需知道自己是在访问代理对象还是实际对象,代理对象通过与实际对象相同的接口来提供服务,提供了透明的访问方式。
  2. 增强功能: 通过代理对象,能够在不改变目标对象的情况下,给目标对象添加额外的功能(如延迟加载、缓存、权限控制等)。
  3. 延迟加载: 可以在代理对象中实现延迟加载,直到真正需要时才初始化目标对象。
  4. 控制访问: 可以在代理对象中控制对真实对象的访问,比如添加安全检查、日志记录等功能。

3. 代理模式的缺点

  1. 性能开销: 代理模式可能会引入一些额外的性能开销,特别是代理对象需要进行额外的逻辑处理时,可能会影响性能。
  2. 增加复杂性: 代理模式会增加系统的复杂性,因为它引入了代理对象并且需要协调代理和目标对象之间的关系。

4. 代理模式的实现

场景示例:银行账户的访问控制

假设我们有一个银行账户类,用户可以通过该类来进行资金的存取操作。为了安全起见,银行希望对这些操作进行日志记录和权限控制。因此,我们可以使用代理模式来实现:

  1. 客户端直接操作代理对象。
  2. 代理对象在执行操作前进行权限检查、日志记录等,然后调用真实的银行账户操作。

1) 定义主题接口

首先定义一个接口,表示银行账户的基本操作。

// 主题接口
public interface BankAccount {
    void deposit(double amount);
    void withdraw(double amount);
    double getBalance();
}

2) 实现真实对象

真实对象实现了 BankAccount 接口,并提供具体的业务操作。

// 真实对象
public class RealBankAccount implements BankAccount {
    private double balance;

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

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

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

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

3) 实现代理对象

代理对象实现了 BankAccount 接口,并在方法调用时添加额外的功能(如权限控制和日志记录)。

// 代理对象
public class BankAccountProxy implements BankAccount {
    private RealBankAccount realBankAccount;

    public BankAccountProxy(double initialBalance) {
        realBankAccount = new RealBankAccount(initialBalance);
    }

    @Override
    public void deposit(double amount) {
        logAction("deposit", amount);
        realBankAccount.deposit(amount);
    }

    @Override
    public void withdraw(double amount) {
        logAction("withdraw", amount);
        realBankAccount.withdraw(amount);
    }

    @Override
    public double getBalance() {
        return realBankAccount.getBalance();
    }

    private void logAction(String action, double amount) {
        System.out.println("Logging: Action = " + action + ", Amount = " + amount);
    }
}

4) 客户端代码

客户端通过代理对象来进行存款和取款操作,而不需要直接与真实对象交互。

public class Client {
    public static void main(String[] args) {
        BankAccount account = new BankAccountProxy(1000);

        // 客户端通过代理对象进行操作
        account.deposit(500);
        account.withdraw(200);
        account.withdraw(1000);
        System.out.println("Final balance: " + account.getBalance());
    }
}

运行结果:
Logging: Action = deposit, Amount = 500.0
Deposited 500.0, New balance: 1500.0
Logging: Action = withdraw, Amount = 200.0
Withdrew 200.0, New balance: 1300.0
Logging: Action = withdraw, Amount = 1000.0
Withdrew 1000.0, New balance: 300.0
Final balance: 300.0

在这个例子中,代理对象 BankAccountProxy 在调用真实银行账户的操作之前进行了日志记录,增加了访问控制的功能,而客户端并不需要知道这些细节。


5. 代理模式的应用场景

  1. 远程代理: 当一个对象位于不同的地址空间时,使用代理对象来控制对远程对象的访问(例如,远程方法调用(RMI))。
  2. 虚拟代理: 延迟加载资源或对象,直到真正需要时才创建。例如,图像的加载和显示通常使用虚拟代理模式来避免不必要的资源加载。
  3. 保护代理: 控制对真实对象的访问权限,比如验证用户的身份或权限。
  4. 智能代理: 代理对象可以添加一些额外的功能,比如引用计数、缓存、内存管理等。
  5. 缓存代理: 在代理中实现缓存功能,减少真实对象的重复访问。

6. 代理模式与其他模式的比较

设计模式主要用途与代理模式的区别
装饰模式动态地给对象添加额外的功能。装饰模式通常是给对象添加行为,而代理模式是控制对对象的访问。
适配器模式将接口转化为客户端期望的接口,使得两个不兼容的接口可以协同工作。适配器模式用于接口适配,而代理模式用于控制访问。
单例模式确保类只有一个实例并提供全局访问点。单例模式通常是限制实例数量,而代理模式是控制对对象的访问。

7. 总结

代理模式是一种非常实用的设计模式,它通过引入代理对象来控制对真实对象的访问,从而为客户端提供附加的功能,比如权限控制、延迟加载、日志记录等。代理模式可以透明地为客户端提供真实对象的功能,同时避免客户端直接依赖目标对象,从而使得系统更具灵活性和可扩展性。

在实际应用中,代理模式可以应用于许多场景,如远程代理、虚拟代理、保护代理和智能代理等。通过使用代理模式,能够在不改变目标对象的情况下,为目标对象添加额外的功能,同时有效地控制对象的访问。

;