代理模式与Kotlin中的代理模式
图片来自必应
代理模式(Proxy):Proxy是“代理人”的意思,意思是有一些工作不一定需要本人去完成,而是可以委托代理人去完成。代理模式属于结构型模式,相当于我们为委托对象提供了一个访问控制,当外部客户端想要访问委托对象的时候,通过代理对象访问。
Java中的代理模式(静态代理与动态代理)
首先我们来看一下代理模式的UML类图:
从UML类图中可以看到,当客户端想要访问一个对象的时候,可以通过访问Proxy这个代理类,这样就达到了访问控制的目的。下面我们通过一个例子来说明一下代理模式的具体作用。比如现在有一个图书馆,我们可以从图书馆中借书或者买书,那么我们可以定义一个接口类型如下,对比UML类图中的Subject:
1 | public interface ILibrary{ |
那么我们可以定义一个ILibrary的实现类,对比UML类图中的RealSubject:
1 | public class Library implements ILibrary{ |
那么当我们定义好Library这个实例后,比如我们现在想要在借书之前做一个标记“书被借出了”。那么我们有几种方式可以去实现,比如我们可以直接修改Library,但可能下次又会加一个类似“哪种书被借出了”、“第几区第几排的书被借出了”类似的需求,那么我们就就要不断的去修改原有的类,或者不断的继承原有的类去修改方法,所以我们直接修改原有的类是不可行的。这时候,我们就需要一个代理类,帮助我们去实现类似的方法、对比UML类图中的Proxy:
1 | public class LibraryProxy implements ILibrary{ |
那么我们可以在使用中:
1 | public class ProxyTest{ |
上面代码输出的结果为:
1 | buy book |
从上面的代码可以看出,我们通过聚合的方法,通过代理持有一个委托类的实例,客户端通过代理类访问委托类的对象,从而实现一些操作。这就是代理模式的简单实现。那么还有个问题,如果我们想在买书的上面也加上类似的需求,我们或许可以在代理中直接加上代码,但如果我们在ILibrary中再加入一些方法,比如“还书”、“下架书”、“上架书”等等等的方法,我们都要去代理中给这些方法写上同样的代码,很麻烦,这时候我们就可以使用动态代理去解决这个问题。如果要使用动态代理,我们来新建一个类,名字叫做DynamicLibraryHandler
1 | public class DynamicLibraryHandler implements InvocationHandler { |
然后修改一下我们的main方法:
1 | public class ProxyTest{ |
上面的代码输出结果为:
1 | ---pre--- |
可以看到,我们并没有实现代理类,也对每个方法都加上了我们想要的需求,关键点就在InvocationHandler 和 Proxy.newProxyInstance() 上,那么首先来看一下InvocationHandler
的源码:
1 | /** |
从InvocationHandler
的注释可以看出来,当生成的代理类调用它的方法的时候,该方法将会被InvocationHandler
转发,调用其invoke()
方法,实现代理。
从上面的例子中可以看出,代理类的创建是通过 Proxy.newProxyInstance()
创建的,再来看下其源码:
1 | public static Object newProxyInstance(ClassLoader loader, |
从上面的源码中可以看出来,代理类是通过 getProxyClass0(loader, intfs)
获取的,那么再来看一下 getProxyClass0(loader, intfs)
的源码:
1 | private static Class<?> getProxyClass0(ClassLoader loader, |
从源码看出来是通过 proxyClassCache
获取的:
1 | private static final ProxyClassFactory implements BiFunction<ClassLoader, Class<?>[], Class<?>>{ |
可以看出来,通过native的 generateProxy()
方法生成的代理类。动态代理在很多地方都有用到,比如我们常用的网络框架Retrofit。
Kotlin中的委托
Kotlin中的委托模式的实现方式,在代码上显得更加清晰一些,我们首先来引用一段官方的例子:
1 | interface Base { |
这里我们看到kotlin中使用到了一个by 的关键字,by就表示b就是我们在前面所说的UML类图中的RealSubject。我们可以将这段代码使用Android Studio转为Java代码看一下就可以看出来了:
1 | public final class Derived implements Base { |
上面代码已经很清楚了,和我们的代理模式写法一样。我们把上面的代码稍作修改:
1 | interface Base { |
这样我们的程序输出结果就是
1 | 10 |