设计模式之结构型模式---代理模式

1.概述

代理模式是结构型模式的一种,结构型模式描述的是如何将类和对象按照某种布局组成更大的结构,它分为类结构型和对象结构型,类结构型主要都会哦那个继承机制来组织接口和类,而对象结构型主要是使用组合和聚合来组合对象。代理模式分为两种,分别是静态代理和动态代理,代理模式主要是为其他对象提供一种代理以控制对这个对象的访问,如下所示:

在这里插入图片描述
如上图所示,假设我们客户端A想访问目标C,但是无法直接访问,我们就可以使用一个代理B代替我们去访问目标C,比如我们想买一个国外的商品,这个商品在国内买不到,而我们又没有时间去国外,或者是语言不通,这时候我们会请代购帮我们买,这里的客户端A指的就是我们自己,代理就是代购,目标C就是我们要买的商品。

为了更加浅显地展示代理模式的原理和实现,参考B站上一个博主的买U盘的例子来介绍本文,博主名字具体忘了,大家想看视频教学的可以到B站搜索“代理模式”关键词学习。

2.代理模式类图

在这里插入图片描述


抽象主题类: 上图中的interface 接口,在这个接口中定义我们想要做的事情,我们使用doSomeThing()表示我们想要做的事情。这里官方点说就是,通过接口或者抽象类声明真实主题和代理对象实现的业务方法。
真实主题类 : 上图中的RealSubject,实现了抽象主题类中的具体业务。
代理类: 上图中的Proxy, 实现了与真实主题类相同的接口,它包含对真实主题类的引用,可以控制访问或者是扩展真实主题的功能

3.应用场景

3.1 功能增强

代理模式的应用场景特别多,有一段时间,Android的插件化功能就需要用到代理模式,简单的说就是通过继承系统的某个类,然后重写我们想要操作的方法,使用动态代理,让系统调用我们实现的类,以完成我们想要添加的功能,所以代理模式可以做功能增强,也就是说可以在现有的功能基础上,增加额外的功能。如下图所示:
在这里插入图片描述
功能增强意思就如上图所示,比如有一个原始功能类定义了码农只能搬砖(写代码),但是后面码农升级了,在上班之前可以去送几单外卖,下班之后可以去开滴滴。如果码农直接去使用原始功能类,那么增加这些功能就得修改原始功能类,不符合开闭原则;但是使用代理类就可以在之前原始功能类的基础上,增加新的功能,这就是代理模式的功能增强

3.2 控制访问

控制访问也就是我们可以使用代理类去控制客户端类访问目标,防止客户端类带有危险请求导致目标类受到破坏,如下图所示:

在这里插入图片描述

4.实现

实现代理模式的方式有两种,分别是静态代理和动态代理,在这里我们使用文章开头的卖U盘的例子实现,我们模拟用户购买U盘的行为:
首先用户是客户端类,进行购买U盘的行为,商家是代理类,代理某个品牌的U盘,而厂家是目标类,表示卖U盘这个行为。三者的关系如下所示:

在这里插入图片描述
如上图所示,商家和厂家的目的都是卖U盘,他们完成的功能是一致的,都是卖U盘这个行为,下面我们一起来看静态代理和动态代理两种模式的实现

4.1 静态代理的实现

静态代理的代理类是自己手工实现的,我们自己创建一个Java类表示代理类,同时要代理的目标是确定的,静态代理的特点就是实现简单,容易理解。

4.1.1 实现静态代理模式的步骤

(1)创建接口,定义卖U盘的方法,表示厂家和商家要做的事情,即卖U盘

public interface IUSBShop {
    
    
     int sellUSB();
}

(2)创建厂家类,实现步骤(1)的接口

public class USBFactory implements IUSBShop {
    
    
    @Override
    public int sellUSB() {
    
    
        return 100;//返回100表示厂家的U盘定价
    }
}

(3)创建商家类,也就是代理类,实现步骤(1)的接口,实现卖U盘的方法,并持有一个厂家类的引用

public class USBProxy implements IUSBShop {
    
    
    private IUSBShop mIUSBShop;

    public USBProxy(IUSBShop iusbShop){
    
    
        this.mIUSBShop = iusbShop;
    }
    @Override
    public int sellUSB() {
    
    
        int price = mIUSBShop.sellUSB();//厂家建议零售价
        System.out.println("厂家建议的零售价是:" + price);
        //功能增强,商家的定价
        int finalPrice = price + 40;
        return finalPrice;
    }
}

如上面代码所示代理类在拿到厂家的定价后,扩展定义了自己家的定价

(4)创建客户端类,调用商家的方法购买U盘

public class Client {
    
    
    public static void main(String[] args) {
    
    
        IUSBShop iusbShop = new USBFactory();
        USBProxy usbProxy = new USBProxy(iusbShop);
        System.out.println("商家不接受厂家建议,最终卖一个USB的价格是:"+usbProxy.sellUSB());
    }
}

4.1.2 静态代理的缺点

使用静态代理时,假设项目中的需要代理的目标类很多时,代理类可能会需要成倍的增加,也就是说,增加一个目标类就得手动增加一个代理类与之对应,而且假设接口中的功能增加或者是修改了,会影响众多的实现类,即厂家类代理类都需要修改。而解决这些的办法就是使用动态代理

4.2 动态代理的实现

动态代理是指在程序执行的过程中,使用JDK的反射机制创建代理类的对象,并动态指定要代理的目标类,动态代理的实现方式有两种,一是使用JDK的动态代理(本文的实现方式)即使用Java发射包中的类和接口实现动态代理的功能,在Java的反射包java.lang,reflect中,使用InvocationHandler,Method,Proxy三个类实现动态代理,第二种就是使用CgLib实现动态代理

当静态代理中目标类很多时,就可以使用动态代理,因为动态代理即使目标类很多,代理类的数量也可以很少,而且当修改了接口中的方法时,不会影响代理类。

注释:CgLib(Code Generation Librart)是实现动态代理的第三方工具库,它的原理是继承,CbLib通过继承目标类,创建它的子类,在子类中重写父类中同名的方法,实现功能的修改,因为CgLib是继承重写方法,所以要求目标类不能是final的,如Mybatis Spring中都有使用导CgLib

4.2.1 Java JDK 动态代理的实现

在使用JDK的动态代理之前,我们需要了解三个类,即Method,InvocationHandler,Proxy.

Method

代表类中的方法,method中有一个invoke()方法,表示方法的调用,invoke方法需要2个参数:
1.object: 表示需要执行的方法所属的对象
2.object args :执行方法时需要的参数值

InvocationHandler

这个类中就一个invoke方法,这个方法表示代理对象要执行的功能代码,代理类要完成的功能写在这个invoke方法中,就如我们前面说的代理类主要完成的作用就是目标方法的调用和功能的扩展增强,这些逻辑就是写在这个类的invoke()方法中

invoke方法原型如下:

invoke(Object proxy, Method method, Object[] args)

参数解释:
(1)proxy:Jdk创建的代理对象,不需要赋值
(2)method: 目标类的方法,由JDK提供
(3)args: 目标类方法中的参数

Proxy

这个类作用比较简单,作用就是创建代理对象,在该类中有一个用于创建代理类对象的方法newProxyInstance()方法原型如下:

public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)

参数解释:
(1)ClassLoader:目标对象的类加载器,使用反射获取
(2)interface:目标对象实现的接口,反射获取
(3)InvocationHandler: 我们自己实现的,代理类要完成的功能

这个方法的返回值就是一个代理对象

4.2.2 JDK动态代理的使用方法

(1)创建一个类实现Invocationhandler,重写invoke()方法,把要完成的功能写在此方法中
(2)在我们重写的invoke()方法中有个Method类型的参数,这里就是表示目标类中的方法,我们可以通过Method执行某个目标类中的方法,使用method.invoke(目标对象,方法参数)
(3)使用Proxy类创建代理对象,调用目标类的方法完成定义的功能。

4.2.3 JDK动态代理的实现步骤

我们还是以商家卖U盘为例介绍JDK动态代理的实现步骤
(1)创建接口定义目标类要完成的功能

public interface IUSBShop {
    
    
     int sellUSB();
}

(2)创建目标类实现步骤(1)中创建的接口

public class USBFactory implements IUSBShop {
    
    
    @Override
    public int sellUSB() {
    
    
        return 100;
    }
}

(3) 创建一个类实现InvocationHandler接口,在该接口的invoke()方法中完成代理类的功能,即调用目标方法,增强功能

public class MyInvocationHandler implements InvocationHandler {
    
    
    private Object usbFactory;

    public void setUSBFactory(Object usbFactory) {
    
    
        this.usbFactory = usbFactory;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
    
        int price = (int) method.invoke(usbFactory, args);
        System.out.println("厂家零售价: " + price);
        int finalprice = price + 40;
        System.out.println("最终价格: " + finalprice);

        return finalprice;
    }
}

(4)使用Proxy类的静态方法,创建代理对象,并把返回值转为接口类型,调用目标类方法,完成买U盘的行为

public class Client {
    
    
    public static void main(String[] args) {
    
    
        IUSBShop iusbShop = new USBFactory();
        MyInvocationHandler myInvocationHandler = new MyInvocationHandler();
        myInvocationHandler.setUSBFactory(iusbShop);
        IUSBShop proxy = (IUSBShop) Proxy.newProxyInstance(USBFactory.class.getClassLoader(),
                iusbShop.getClass().getInterfaces(), myInvocationHandler
        );

        System.out.println("FinalResult :" + proxy.sellUSB());
    }
}

动态代理可以在不改变原来目标方法功能的前提下,在代理中增强扩展自己的代码

猜你喜欢

转载自blog.csdn.net/zxj2589/article/details/131543272