设计模式之代理模式

设计模式之代理模式

1. 概括

在编写代码的过程中,经常要对原有的代码进行修改,【增强】
但是如果直接修改源代码,长此以往代码应付变得,冗余、复杂、庞大
使用代理模式可以在不修改原有代码的基础上,并对原有代码的增强,并且增加了代码的隔离性
在调用时调用代理类的方法,由代理方法调用最终对象方法,在调用之前和之后,并且可以进行一定的增强

2. 结构

  • 服务抽象接口:声明的服务方法,为客户端的最终调用方法
  • 服务实体:实现服务接口,重写方法中的逻辑
  • 代理类:同样实现服务接口,在重写的方法中调用最终执行的(服务实体)方法,在调用之前和之后,可以进行一定的增强

3. 使用场景

  • 访问控制:不想直接访问某个对象中的方法
  • 增强:对原有代码执行前后进行其它的操作,比如:打印日志、处理缓存等

4. 静态代理

  • 示例:火车站卖票系统,卖票系统是接口,火车站是服务实体对象
  • 使用代理对象访问,代理在 ”买票“ 之前,要收取服务费用
  • 创建服务接口
public interface SellTickets {
    void sell();
}
  • 服务实体
public class TrainStation implements SellTickets{
    @Override
    public void sell() {
        System.out.println("火车站卖票");
    }
}
  • 代理类,同样实现卖票系统接口
public class ProxyPoint implements SellTickets{

	// 被代理的对象,也就是最终服务
    private SellTickets sellTickets;
    public void setSellTickets(SellTickets sellTickets) {
        this.sellTickets = sellTickets;
    }

    @Override
    public void sell() {
        // 增强
        System.out.println("收取代理费用(静态代理)");
        sellTickets.sell();
    }
}
  • 调用
public class Client {
    public static void main(String[] args) {
        // 创建代理对象
        ProxyPoint proxy = new ProxyPoint();
        // 设置最终调用的真实对象,
        TrainStation trainStation = new TrainStation();
        proxy.setSellTickets(trainStation);

        // 调用代理对象中 增强后 的方法
        proxy.sell();
    }
}

5. 动态代理

如果在最终的调用对象中,不止一个方法,有多个方法需要我们代理增强,并且有可能还会是同样的增逻辑,比如,打印日志;那么静态代理显然会增加代理类的复杂度、冗余性
这里就可以使用动态代理

5.1 JDK动态代理

  • JDK的动态代理基于接口实现,需要我们提供一个服务抽象接口【必需
  • 同样是上面的服务接口与实现类。代理类可以这么写
public class ProxyFactory {

    private SellTickets sellTickets;

    public void setSellTickets(SellTickets sellTickets) {
        this.sellTickets = sellTickets;
    }

	// 返回参数类型的接口
    public SellTickets getProxyObject() {
        SellTickets proxyInstance = (SellTickets) Proxy.newProxyInstance(
                sellTickets.getClass().getClassLoader(),
                sellTickets.getClass().getInterfaces(),
                (proxy, method, args) -> {
                    System.out.println("收取了代理费用(jdk动态代理)"); // 增强
                    Object obj = method.invoke(sellTickets, args);
                    return obj;
                });
        return proxyInstance;
    }

}
  • 注意:这里的ProxyFactory类不是代理类,而一个可以生成代理对象的工厂,getProxyObject()返回的对象才是代理对象

  • 返回的代理对象与被代理类【同级】的(实现同一接口),所以必需用接口接收

  • Proxy.newProxyInstance方法中有三个参数

    • ClassLoader loader 类加载器,用于加载被代理类
    • Class<?>[] interfaces真实对象所实现的接口,代理模式真实对象和代理对象实现相同的接口
    • InvocationHandler h 代理对象的调用处理程序(回调函数),被代理类中的每个方法都会被这个接口实现类中的invoke方法所调用,其中的参数
      • proxy代理对象,就是Proxy.newProxyInstance方法的返回对象
      • method目标方法,被代理中的每个方法实例
      • args 调用方法的参数列表
  • 客户端

public class Client {

    public static void main(String[] args) {
        // 创建【最终】执行的对象
        TrainStation trainStation = new TrainStation();
        // 创建代理工厂
        ProxyFactory proxyFactory = new ProxyFactory();
        // 工厂中的最终执行设置
        proxyFactory.setSellTickets(trainStation);

        // 获取代理对象,代理对象与最终执行对象是【同级】的
        SellTickets proxy = proxyFactory.getProxyObject();
        // 调用代理 【增强后】 的方法
        proxy.sell();
    }
    
}

5.2 CGLib动态代理

JDK动态代理必需依赖于接口,当我们要代理的类没有实现一个接口时,就可以用CGLib动态代理
CGLib是以继承被代理类的方式实现的,所以被代理类不可被final修饰
依然是上面的例子

  • 现在这个类没有实现任何的接口,我们要实现对它的代理
public class TrainStation {
    public void sell() {
        System.out.println("火车站卖票");
    }
}
  • 导入CGLib的依赖
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version>
</dependency>
  • 代理对象工厂
public class ProxyFactory {

    private TrainStation trainStation;
    public void setTrainStation(TrainStation trainStation) {
        this.trainStation = trainStation;
    }

    public TrainStation getProxy() {
        Enhancer enhancer = new Enhancer();

        enhancer.setSuperclass(trainStation.getClass()); // 设置要代理谁
        // 回调接口
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                System.out.println("收取一定的费用(增强)"); // 增强
                Object obj = method.invoke(trainStation, objects);
                return obj;
            }
        });
        // 创建代理对象
        TrainStation trainStation = (TrainStation) enhancer.create();
        return trainStation;
    }
    
}
  • 回调中的参数
    • o : 被代理对象
    • method : 真实对象中的方法的Method实例
    • args : 方法中的实际参数
    • methodProxy :代理对象中的方法的method实例
public class Client {

    public static void main(String[] args) {
        // 最终 调用对象
        TrainStation trainStation = new TrainStation();

        // 创建获取代理工厂对象
        ProxyFactory proxyFactory = new ProxyFactory();
        proxyFactory.setTrainStation(trainStation);
        // 获取【代理】对象
        TrainStation proxy = proxyFactory.getProxy();
        // 执行 代理中的 增强后的 方法
        proxy.sell();
    }
    
}

5.3 动态代理工厂通用抽取

  • 可以用泛型使代理工厂通用化
  • 其中的回调也可二次抽取
  • JDK动态代理
public class ProxyGenericFactory<T> {

    private T t;
    public void setT(T t) {
        this.t = t;
    }

    public T getProxy() {
        T proxyInstance = (T) Proxy.newProxyInstance(
                t.getClass().getClassLoader(),
                t.getClass().getInterfaces(),
                (proxy, method, args) -> {
                    System.out.println("收取了代理费用(jdk动态代理)");  // 增强
                    Object obj = method.invoke(t, args);
                    return obj;
                }
        );
        return proxyInstance;
    }

}
  • CGLib
public class ProxyGenericFactory<T> {

    private T t;
    public void setT(T t) {
        this.t = t;
    }

    public T getProxy() {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(t.getClass());
        enhancer.setCallback((MethodInterceptor) (o, method, objects, methodProxy) -> {
            System.out.println("增强"); // 增强
            Object obj = method.invoke(t, objects);
            return obj;
        });
        T proxy = (T) enhancer.create();
        return proxy;
    }

}
代理模式是一种结构型设计模式,它提供一个代理对象来代表另一个对象。在代理模式中,有一个被称为实际对象(Subject)和一个被称为代理对象(Proxy)的中介,代理对象持有实际对象的引用,并且可以控制对实际对象的访问。代理模式的主要目的是在不修改原始对象的情况下,为原始对象添加额外的逻辑处理。 代理模式分为多种类型,如远程代理、虚拟代理、保护代理等,它们各自有不同的应用场景: - 远程代理:为远程对象提供一个本地代表。 - 虚拟代理:根据需要创建开销大的对象,通过虚拟代理控制访问这些对象的过程。 - 保护代理:控制对原始对象的访问权限,例如进行权限检查。 代理模式的优点包括: 1. 能够控制对真实对象的访问,并在访问前后添加额外的逻辑。 2. 可以通过代理对象实现延迟加载,即在实际需要时才创建真实对象。 3. 增强了对真实对象的封装,并且可以避免对真实对象的重复引用。 在C#中实现代理模式通常涉及以下步骤: 1. 定义一个接口或抽象类,声明真实对象和代理对象需要实现的方法。 2. 实现真实对象的类,按照接口或抽象类的要求实现具体方法。 3. 实现代理类,它同样实现接口或抽象类,并在方法中持有真实对象的引用,通过调用真实对象的方法来执行所需的操作,同时可以添加额外的逻辑。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值