likes
comments
collection
share

Android-我对代理模式的理解

作者站长头像
站长
· 阅读数 20

以下业务场景不大现实,我这里只是提供一种思路 想象一种场景:有一天,产品经理让你记录某些地方的行为日志并且存储到本地方便查阅,你可能会写下如下代码:

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
评论
请登录