注册

Android-我对代理模式的理解

以下业务场景不大现实,我这里只是提供一种思路

想象一种场景:有一天,产品经理让你记录某些地方的行为日志并且存储到本地方便查阅,你可能会写下如下代码:

interface ILogger {

fun logInfo(action: String)

fun logError(action: String)
}


class Logger : ILogger{

override fun logInfo(action: String) {
//存储到本地
saveToLocalFile(action)
}

override fun logError(action: String) {
//存储到本地
saveToLocalFile(action)
}

private fun saveToLocalFile(action: String) {}
}

当需要调用的时候:

val logger: ILogger = Logger()
logger.logError("出现问题")

当然了,你更大概率是考虑用一个单例类直接调用,而不是每次都这样写。

假如某天换了个产品经理,要求你在这些存储日志之前,先将日志上传到服务器,存储日志后,做一个埋点记录

class Logger : ILogger{

override fun logInfo(action: String) {
//上传到服务器
upLoadToCloud(action)
//存储到本地
saveToLocalFile(action)
//埋点
eventTracking(action)
}

override fun logError(action: String) {
//上传到服务器
upLoadToCloud(action)
//存储到本地
saveToLocalFile(action)
//埋点
eventTracking()
}

private fun saveToLocalFile(action: String) {}

private fun upLoadToCloud(action: String) {}

private fun eventTracking() {}
}

设计模式讲究一个职责单一,那么以上代码最直观的就是不同的功能耦合在一起。


什么是代理模式


一句话解释就是:在不改变原有功能的基础上,通过代理类扩展新的功能,使得功能之间解耦,或者框架和业务之间解耦,有点装饰器模式的味道。


静态代理

interface ILogger {

fun logInfo(action: String)

fun logError(action: String)
}

class Logger : ILogger{

override fun logInfo(action: String) {
//存储到本地
saveToLocalFile(action)
}

override fun logError(action: String) {

//存储到本地
saveToLocalFile(action)
}

private fun saveToLocalFile(action: String) {}

}

class LoggerProxy(val logger: Logger) : ILogger {

override fun logInfo(action: String) {
//上传到服务器
upLoadToCloud(action)
//通过传进来的logger对象来调用原来的实现方法
logger.logInfo(action)
//埋点
eventTracking(action)
}

override fun logError(action: String) {
//上传到服务器
upLoadToCloud(action)
//通过委托logger对象来调用原来的实现方法
logger.logError(action)
//埋点
eventTracking(action)
}

private fun upLoadToCloud(action: String) {}

private fun eventTracking(action: String) {}
}

//使用方式
val logger: ILogger = LoggerProxy(Logger())
logger.logError("出错了")

在第25行,我们新添加了一个新的LoggerProxy代理类同样的实现了ILogger接口,在两个方法中,我们按顺序完成了功能的调用,将上传到服务器和埋点的逻辑和存储到本地的逻辑进行了分离,代理类LoggerProxy在业务的执行前后附加了其他的逻辑。

看到这你可能会觉得,有点脱裤子放屁了。确实,当前代码量特别小,对于当前代码体现的可能不太明显,如果你正在一个设计相对大型的框架,业务和框架代码的分离显得就相对重要了。

作为一种设计思想,他提供的是一种思路,让你写出来的不是面向过程的代码,有好有坏,当然在实际项目中不要为了设计模式而设计模式,不然就适得其反了,写出来的代码可读性差。


动态代理


对于静态代理,上面的代码中我们在代理类中的前后加了两个不同的功能,这两个相对职责不同的功能耦合在了一起,我由于偷懒没将其中的一个功能拆走,正常情况是应该再写一个代理类去做相同的一部分操作,如果功能更多的话就要写更多的代理类,繁琐度可想而知。

再一个,静态代理是在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了。而动态代理类的源码是在程序运行期间由JVM根据反射等机制动态的生成,所以不存在代理类的字节码文件。代理类和委托类的关系是在程序运行时确定。

class Logger : ILogger{

override fun logInfo(action: String) {
println("存储到本地: $action")
saveToLocalFile(action)
}

override fun logError(action: String) {
println("存储到本地: $action")
saveToLocalFile(action)
}

private fun saveToLocalFile(action: String) {}

}


class LoggerProxy(private val target: ILogger): InvocationHandler {

fun createProxy() = Proxy.newProxyInstance(
ILogger::class.java.classLoader,
arrayOf<Class<*>>(ILogger::class.java),
LoggerProxy(target)
) as ILogger

override fun invoke(proxy: Any?, method: Method?, args: Array<out Any>?): Any? {
val action = args!![0].toString()
if (method?.name == "logInfo") {
uploadToCloud(action)
target.logInfo(action)
eventTracking(action)
} else if (method?.name == "logError") {
uploadToCloud(action)
target.logError(action)
eventTracking(action)
}
return null
}

private fun uploadToCloud(action: String) {
println("上传数据到服务器")
}

private fun eventTracking(action: String) {
println("埋点")
}
}

interface ILogger {

fun logInfo(action: String)

fun logError(action: String)
}

调用方式
val proxy = LoggerProxy(Logger())
proxy.createProxy().logError("出错了")

打印顺序
1. 上传数据到服务器
2. 存储到本地: 出错了
3. 埋点


  1. 在动态代理中,当我们通过createProxy()创建代理对象后,调用logError或logInfo方法的时候
  2. 代理对象的invoke()方法会被调用
  3. 由于我们传入的只有action这个参数,在invoke方法中,可通过args[0]来获取传入的数据;通过method.name获取待执行的方法名,以此来判断逻辑的走向

代理的创建方式createProxy()方法中的代码大部分都是固定的。


总结


静态代理:静态代理在编译时期就已经确定代理类的代码,代理类和被代理类在编译时就已经确定;如果需要扩展的功能越来越多,静态代理的缺点很明显就是要写大量的代理类,管理和维护都不太方便。

动态代理:动态代理在运行时动态生成代理对象,关系灵活,由于是在运行时动态的生成代理类,动态代理解决了静态代理大量代理类的问题,但是有个新的问题就是反射相对耗时一点。


我们常用的Retrofit就有用到动态代理,感兴趣的同学可以去深入了解下,这边就不过多讲解,包括AOP(面向切面编程),动态权限申请等等。


作者:盖乌
链接:https://juejin.cn/post/7247405681636884538
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

0 个评论

要回复文章请先登录注册