【学习笔记】结合代码理解设计模式 —— 代理模式(静态代理、动态代理、延伸)

前言:笔记基于狂神设计模式视频、《大话设计模式》观后而写
(最近一直在更新之前的刷题博客,今天久违地更新一篇新博客啦~)

什么是代理模式

一. 代理模式简介

代理模式是很常用的设计模式噢~同时也有很多的类型,适当学习一下是非常有必要的!

  • 定义:为其他对象提供一种代理,以控制对这个对象的访问。
    可以理解为:在访问对象时引入一定程度的间接性,由这种间接性来附加多种用途
  • UML 图:代理组合真实角色,代理和真实对色都继承公共接口
    在这里插入图片描述
  • 举个例子:经纪人(代理)、明星(真实角色),都继承“接戏”接口。想找明星演戏的话,就得通过经纪人的“接戏”(把明星想象成经纪人的私有化对象)。这样做的话,经纪人可以在自己的“接戏”方法上添加筛选、谈薪等任务,而明星只需关注自己的业务即可。
  • 在上面这个例子里,代理模式的好处就有体现出来了。真实角色更加地专一,并且在代理角色的拓展,也遵守了OOP原则的开放封闭原则
  • 代理模式的分类
    1. 静态代理
    2. 动态代理(这两种类型,下文有结合代码使用、讲解)
    3. 远程代理:为一个对象在不同地址空间提供局部代表,用于隐藏一个对象存在于不同地址空间的事实(比如调用另一台PC的方法)
    4. 虚拟代理:用于存放实例化需要很长时间的真实对象,可以达到性能的最优化。比如打开哔哩哔哩,卡顿情况先显示文字,而图片、视频流可能就只是一个白框之类的玩意,之后再逐渐加载出来,这里就用到了虚拟代理
    5. 安全代理、智能指引等其他分类,感兴趣可以去查查~

二. 静态代理模式

  • 角色分类
    1. 抽象角色:接口 or 抽象类。
    2. 真实角色:就被代理的角色
    3. 代理角色:真实角色的代理,一般会加一些附属操作。
    4. 客户:访问代理角色的人。
  • 好处:
    1. 使真实角色的操作更加纯粹,不用关注一些公共的业务。
    2. 公共业务交给代理角色,实现了业务的分工
    3. 公共业务发生拓展的时候,方便管理
  • 缺点:一个真实角色对应一个代理角色,代码量增大,效率变低
  • 样例代码:房东、中介、客户、租房(试着想想对应上面的什么角色吧~)
// ps:public class 不要介意,这里我是把不同文件的代码直接拷过来了。
// 1. 公共接口
public interface Rent {
    public void rent();
}

// 2. 房东(真实角色)
public class Host implements Rent{
    @Override
    public void rent() {
        System.out.println("房东:要出租房子");
    }
}

// 3. 中介(代理角色)
public class MyProxy implements Rent {
    // 采用"组合"方式
    private Host host;

    MyProxy() {
    }

    MyProxy(Host host) {
        this.host = host;
    }

    @Override
    public void rent() {
        // 可以添加一些更多的内容,而主体(被代理类)却不用关心这些事情,只需要做好自己的事即可。
        // 满足开放封闭原则
        System.out.println("中介:带着看房");
        host.rent();
        System.out.println("中介:签合同");
    }
}

// 4. 客户:访问代理角色
public class Client {
    public static void main(String[] args) {
        // 只需要找中介即可,不用管房东
        Host host = new Host();
        MyProxy MyProxy = new MyProxy(host);
        MyProxy.rent();
    }
}

三. 动态代理模式

为了解决上面静态代理模式的缺点,这里又有了动态代理模式~
(这一块不太好理解,我写得估计也不太详细,建议再看一下视频、或者其他博客)

  • 原理:动态代理基于反射机制实现。
  • 和静态代理的区别
    1. 静态代理:实现接口,再通过接口实现类的实例来代理。
    2. 动态代理:通过反射,造出一个接口类的实例
      (再原理一点,就是通过反射先造出一个带构造方法的,接口的克隆体,再通过这个克隆体的构造方法,生成接口实例)
  • 使用到的类:
    1. Proxy:提供用于创建动态代理类和实例的静态方法,生成一个
    2. InvocationHandler:每个代理实例都具有一个关联的调用处理程序,调用代理实例的方法时,会被指派到其调用处理程序的 invoke 方法上。
    3. com.sun.proxy.$Proxy0 :由 Proxy::newProxyInstance() 生成的代理类。实现了传入接口的每一个方法,以及Object方法。并且统一调用了InvocationHandler 的 invoke() 方法。
  • 好处
    1. 静态代理的好处
    2. 一个动态代理类代理的是一个接口,一般就是对应的一类业务。规避了静态代理的缺点
    3. 在原始类和接口未知时,就确定代理类的代理行为。灵活。
  • 样例代码:还是房东中介的例子噢
// 继承 InvocationHandler 接口,此时 this 就是一个 InvocationHandler
public class ProxyInvocationHandler implements InvocationHandler {
    
    // 被代理的接口
    private Rent rent;
    
    public void setRent(Rent rent) {
        this.rent = rent;
    }
    
    // 生成代理类
    public Object getMyProxy() {
        return Proxy.newProxyInstance(rent.getClass().getClassLoader(), rent.getClass().getInterfaces(), this);
    }
    
    // InvocationHandler 对应的方法 invoke,用于处理代理实例,并且返回结果。被 $Proxy0 的方法调用
    @Override
    public Object invoke(Object Proxy, Method method, Object[] args) throws Throwable {
        // Method 也是反射包下的类,和反射相关。
        System.out.println("动态代理来了噢!");
        Object res = method.invoke(rent, args);
        return res;
    }
}

public class Client {
    public static void main(String[] args) {
        // 只需要找中介即可,不用管房东
        Host host = new Host();
        
        ProxyInvocationHandler proxyInvocationHandler = new ProxyInvocationHandler();
        // 放入被代理对象
        proxyInvocationHandler.setRent(host);
        Rent proxy = (Rent)proxyInvocationHandler.getMyProxy();
        // 这里的 rent,之后会执行 invoke(此时参数 Method 就是 rent)
        // “代理对象”执行“接口方法”,然后指派到对应的 InvocationHandler 的 invoke 上
        proxy.rent();
    }
}
  • 整理:无注释,一个文件不到30行,写完整个动态代理例子
public interface Rent {
    void rent();
}

public class RentDynamicProxy implements InvocationHandler {

    Rent rent;

    RentDynamicProxy(Rent rent) {
        this.rent = rent;
    }

    Object getProxy() {
        return Proxy.newProxyInstance(rent.getClass().getClassLoader(), rent.getClass().getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("启用动态代理");
        return method.invoke(rent, args);
    }
}

public class Host implements Rent{
    @Override
    public void rent() {
        System.out.println("房东出租");
    }

    public static void main(String[] args) {
        Host host = new Host();
        RentDynamicProxy rentDynamicProxy = new RentDynamicProxy(host);
        Rent proxy = (Rent)rentDynamicProxy.getProxy();
        proxy.rent();
    }
}
万能模版
  • 任何接口都可以用噢
public class BetterProxyInvocationHandler implements InvocationHandler {
    // 1. 被代理的接口
    private Object target;

    // 设置被代理的接口
    public void setTarget(Object target) {
        this.target = target;
    }

    // 2. 生成得到代理类
    public Object getProxy() {
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("执行了 " + method.getName() + " 方法");
        Object res = method.invoke(target, args);
        return res;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值