小蔡学Java

Java之设计模式 (十八)

2023-09-28 23:34 745 0 设计模式 设计模式代理模式

代理模式(Proxy Pattern)

  代理模式是一种结构型设计模式,它为其他对象提供一个代理以控制对该对象的访问。代理是一个具有与原始对象相同的接口的对象,客户端不必知道它与原始对象交互的方式。代理可以拦截对原始对象的访问,并在某些情况下将请求传递给原始对象。   代理模式有两种主要形式:

  • 静态代理:在编译时就已经确定了代理类和被代理类之间的关系,通常需要为每个被代理类都编写一个对应的代理类,并实现相同的接口。 静态代理的优点是简单易懂,缺点是不灵活,代码冗余。

  • 动态代理:在运行时动态生成代理对象,并根据反射机制调用被代理类的方法。 动态代理可以使用Java原生API或者第三方框架来实现,如JDK Proxy、CGLIB、等。 动态代理的优点是灵活高效,缺点是复杂难懂。

使用场景

  代理模式的主要目的是通过代理对象来控制对原始对象的访问,并提供一些额外的功能。比如:

  • 当我们想要隐藏某个类时,可以为其提供代理类。例如,我们想要访问一个远程对象,但是不想暴露其网络细节,就可以使用代理类来封装网络通信

  • 当一个类需要对不同的调用者提供不同的调用权限时,可以使用代理类来实现。例如,我们想要限制某些用户对某些方法的访问,就可以在代理类中进行权限检查。

  • 当我们要扩展某个类的某个功能时,可以使用代理模式。 例如,我们想要在调用某个方法之前或之后添加日志、缓存、事务等功能,就可以在代理类中实现

代码实现

静态代理

  一个简单的 Java 静态代理例子,它模拟了一个银行账户的操作:

// 定义一个账户接口
public interface Account {
    void deposit(double amount);
    void withdraw(double amount);
}
// 实现账户接口的类
public class BankAccount implements Account {
    private double balance;

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

    public void deposit(double amount) {
        balance += amount;
        System.out.println("Deposited " + amount + ", balance is now " + balance);
    }

    public void withdraw(double amount) {
        if (balance >= amount) {
            balance -= amount;
            System.out.println("Withdrew " + amount + ", balance is now " + balance);
        } else {
            System.out.println("Sorry, insufficient balance");
        }
    }
}
// 定义一个账户代理类
public class AccountProxy implements Account {
    private BankAccount bankAccount;

    public AccountProxy(BankAccount bankAccount) {
        this.bankAccount = bankAccount;
    }

    public void deposit(double amount) {
        bankAccount.deposit(amount);
    }

    public void withdraw(double amount) {
        bankAccount.withdraw(amount);
    }
}
// 使用示例
public class Main {
    public static void main(String[] args) {
        // 创建一个银行账户实例
        BankAccount account = new BankAccount(1000.0);

        // 创建一个账户代理实例
        Account accountProxy = new AccountProxy(account);

        // 使用账户代理进行操作
        accountProxy.deposit(500.0);
        accountProxy.withdraw(200.0);
        accountProxy.withdraw(2000.0);
    }
}

  在上面的例子中,BankAccount 是账户接口 Account 的实现类,它负责实际的存款和取款操作。而 AccountProxy 则是账户代理类,它实现了账户接口,并在其中持有一个 BankAccount 实例。在 AccountProxy 的 deposit 和 withdraw 方法中,它会将操作转发给 BankAc。   在 Main 方法中,客户端使用代理来执行操作,而无需直接操作实际对象。

动态代理

**  使用 Java 内置的 java.lang.reflect.Proxy 类来创建动态代理**

// 定义一个账户接口
public interface Account {
    void deposit(double amount);
    void withdraw(double amount);
}
// 实现账户接口的类
public class BankAccount implements Account {
    private double balance;

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

    public void deposit(double amount) {
        balance += amount;
        System.out.println("Deposited " + amount + ", balance is now " + balance);
    }

    public void withdraw(double amount) {
        if (balance >= amount) {
            balance -= amount;
            System.out.println("Withdrew " + amount + ", balance is now " + balance);
        } else {
            System.out.println("Sorry, insufficient balance");
        }
    }
}
// 定义一个账户代理类
public class AccountProxy implements InvocationHandler {
    private BankAccount bankAccount;

    public AccountProxy(BankAccount bankAccount) {
        this.bankAccount = bankAccount;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (method.getName().equals("deposit")) {
            System.out.println("Before deposit");
            method.invoke(bankAccount, args);
            System.out.println("After deposit");
        } else if (method.getName().equals("withdraw")) {
            System.out.println("Before withdraw");
            method.invoke(bankAccount, args);
            System.out.println("After withdraw");
        }
        return null;
    }
}

// 使用示例
public class Main {
    public static void main(String[] args) {
        // 创建一个银行账户实例
        BankAccount account = new BankAccount(1000.0);

        // 创建一个账户代理实例
        AccountProxy accountProxy = new AccountProxy(account);

        // 使用动态代理创建一个代理对象
        Account proxy = (Account) Proxy.newProxyInstance(
                Account.class.getClassLoader(),
                new Class<?>[] { Account.class },
                accountProxy
        );

        // 使用代理对象进行操作
        proxy.deposit(500.0);
        proxy.withdraw(200.0);
        proxy.withdraw(2000.0);
    }
}

  BankAccount 和 AccountProxy 类的定义与静态代理例子相同。不同之处在于,我们创建了一个实现了 InvocationHandler 接口的 AccountProxy 类,它的 invoke 方法用来处理代理对象的方法调用。在 invoke 方法中,我们可以根据被调用的方法名称来添加一些额外的功能。在这个例子中,我们为 deposit 和 withdraw 方法添加了前置和后置操作。   在 Main 类中,创建了一个银行账户实例,并将它传递给了一个账户代理实例。然后,我们使用 Proxy.newProxyInstance 方法创建一个动态代理对象,该对象实现了 Account 接口,并在其方法调用时会调用 AccountProxy 的 invoke 方法。最后,我们使用代理对象进行存款和取款操作。

使用小结

  代理模式在Java中的应用比较广泛,比如Spring的AOP实现、远程RPC调用等。代理模式可以在不修改原始接口的情况下,对目标对象进行增强或者替换

评论( 0 )

  • 博主 Mr Cai
  • 坐标 河南 信阳
  • 标签 Java、SpringBoot、消息中间件、Web、Code爱好者

文章目录