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());
    }
}

动态署理能够在不改动本来方针办法功用的前提下,在署理中增强扩展自己的代码