题记
为什么突然会想到整理下代理模式?emm…因为最近头脑风暴了下自己的知识体系,还是对自己代码架构上的东西不够满意。最近梳理的很多知识体系中,反复都提到了代理模式。
- 比如Hook这个玩意真的是令我又爱又恨,其中对动态代理的渗透,让我对这个代码的设计欲罢不能。给篇参考链接:Android插件化原理解析——Hook机制之动态代理。
- 再比如Binder通信,Android 中的AIDL对代理模式的使用也是精彩绝伦。
1. 为什么叫代理模式
官方解释:一个类代表另一个类的功能。这种类型的设计模式属于结构型模式。
不扯皮解释:加个中介。比如说:我想吃个外卖,我没必要亲自去那家店订餐,我只需要去第三方平台订就可以了,平台里面有那家店的订餐方式,都提供了订餐这个方法,可以帮我去那家店订餐。注意,这里最后做饭的操作仍然是那家实体店,平台只是帮我传递一下消息。
好处? 被代理类的方法属性被保护起来了,我不用修改有关被代理类的代码,直接在代理类中添加一系列的逻辑操作。这不就是开闭原则么?不上一段冠冕堂皇的解释:1、职责清晰。 2、高扩展性。 3、智能化。
概念混淆。这个东西跟装饰者模式很像,但是装饰者主要是装饰 → 添加更多功能,而代理模式的重点是控制 → 开闭原则。
2. 静态代理
文字总结起来甚是困难,不如几行代码来的舒心。这里仍然已订餐为例子(PS:我今晚真的不会再订宵夜了,我已经胖到起飞了。)
- 明确自己需要做的动作(买吃的),创建一个包含待实现动作的接口。
interface BuyFood {
fun buyFood()
}
- 一个实际操作对象(被代理)需要实现这个操作(接口)。
class Shop constructor(var name: String) : BuyFood {
init {
println("Shop:$name")
}
override fun buy() {
println("Shop:买买买")
}
}
- 代理类如果要代理这个操作,不需要用户直接操作被代理的对象,则必须要持有这个对象并且实现相同的方法。
class ProxyPlatform constructor(var name: String) : BuyFood {
var shop: Shop? = null;
override fun buy() {
println("我是XX代理平台")
if (shop == null) {
shop = Shop(name)
}
shop!!.buy()
println("点好了")
}
}
- 测试。
class TestPro {
companion object {
@JvmStatic
fun main(args: Array<String>) {
ProxyPlatform("鸡米花").buy()
}
}
}
3. 动态代理
看了上面的代码,其实感觉有点像小孩子过家家。总不能我每实现一个接口就写一个代理类把?就想到能不能偷懒?一个代理类完成所有功能。这时候就体现出前人的肩膀是多么的稳固。JDK中这些东西其实都是直接封装好的了,我们直接用好了。注意俩个关键支持:
接口java.lang.reflect.InvocationHandler
类java.lang.reflect.Proxy
3.1 InvocationHandler
//...
public interface InvocationHandler {
//...
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
点到源码里面看,注释的定义给的一清二楚,简单摘取点有用信息。
类注释:
- 代理实例需要实现这个接口
- 代理实例调用方法触发
invoke(...)
- JDK1.3开始和入这个类。
方法注释:invoke(...)
- Object proxy被代理对象
- Method method待调用的方法
- Object[] args方法调用时入参
3.1 Proxy
Proxy的源码太长了,这里就不粘贴了。
- 它提供创建动态代理的静态方法类和实例,它也是所有由这些方法创建的动态代理的父类。
- 注意
newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
看到第一个参数的类加载器,基本就可以确定这里也是用到的反射加载。
其实我这里都是废话,仔仔细细读一遍其中的注释就好了。
4. 代码
接口和其实现类一样的,没啥变化。
interface BuyFood {
fun buy()
}
class Shop constructor(var name: String) : BuyFood {
override fun buy() {
println("Shop:买买买")
}
}
继承InvocationHandler
接口的代理平台。这里我们直接传入了被代理的对象,直接扔到invoke
方法里面。
class ProxyHandler constructor(var any: Any) : InvocationHandler {
override fun invoke(proxy: Any?, method: Method?, args: Array<out Any>?): Any? {
var methodName=method?.name;
if (methodName.equals("buy")) {
println("我是XX动态代理平台")
println("需要传递$methodName()接口")
val result = method!!.invoke(any, *args.orEmpty())
println("点好了")
return result
}
return null;
}
}
测试类进行调用。这里有个坑,newProxyInstance(...)
,第一个参数传入真实被代理类的加载器,第二个参数一定要传入待实现的接口类,不能传入已经实现的真实被代理类。
class TestPro {
companion object {
@JvmStatic
fun main(args: Array<String>) {
val shop:Shop= Shop("鸡米花")
//第二个参数注意是待实现接口类数组类型
val proxy=Proxy.newProxyInstance(shop.javaClass.classLoader, arrayOf(BuyFood::class.java),ProxyHandler(shop))as BuyFood
proxy.buy()
}
}
好处显而易见了,这个代理平台根据传入的不同类型,做出不同的动作,可以减少很多代码量。当然,invoke()里面也要加入很多逻辑判断。比如到底传递哪个待实现接口?