代理模式

再聊代理模式前,我们先看下什么场景下我们会需要用到代理模式,以及为什么要用?

举个我们代码中常见的例子:
产品需求:我们需要看到一些指定接口的请求数据,比如请求总耗时时间,上报给服务器,用于了解用户的使用体验。

那我们开始撸代码了

    class LoginRequest {
        fun login() {

            val startTime = System.currentTimeMillis()

            //  ...省略发起登陆请求的代码...

            val endTime = System.currentTimeMillis()

            //上报接口请求的开始时间和结束时间给服务器
            ReportService.report(startTime,endTime,"urlLogin")
        }
    }


    class HomePageRequest {
        fun homePageData() {

            val startTime = System.currentTimeMillis()

            //  ...省略发起首页数据请求的代码...

            val endTime = System.currentTimeMillis()

            //上报接口请求的开始时间和结束时间给服务器
            ReportService.report(startTime,endTime,"urlHomePage")
        }
    }

很明显,上面的写法有2个问题,
1:请求耗时的代码和上报日志的代码完全重复,我们需要在每一个上报的请求中都重复此代码.
2:获取请求耗时的代码和上报的代码,和请求的代码在业务上毫无关联,耦合太高.

为了将上报框架代码和请求业务代码解耦,代理模式就派上用场了!

简单说下什么是代理模式:代理模式的本质就是在不改变原始类(或叫被代理类)代码的情况下,通过引入代理类来给原始类附加功能。了解了代理模式的本质,我们再写改造一下上面的例子也加深理解。

    interface ILoginInterface{
        fun login()
    }

    class LoginRequest : ILoginInterface{
        override fun login() {
            //  ...省略发起登陆请求的代码...
        }
    }
    
    class LoginRequestProxy(private val realLogin:LoginRequest):ILoginInterface{
        override fun login() {
            val startTime = System.currentTimeMillis()

            realLogin.login()

            val endTime = System.currentTimeMillis()

            //上报接口请求的开始时间和结束时间给服务器
            ReportService.report(startTime,endTime,"urlLogin")
        }
    }

通过代码我们可以发现,LoginRequest类中只有登陆请求的业务代码了,获取请求耗时和上报的代码都被我们放置在了LoginRequestProxy中了,这样我们就做到了上报框架代码和业务代码的解耦。这种写法就是静态代理.

但上面的写法也有个问题在于:我们需要给每个需要解耦、需要被代理的类都去创建一个xxxProxy代理的类,这个工作不仅繁琐,同时还增加了维护成本,后续只要我们新增一个接口请求,都要创建一个代理类,实现相同的接口。这听着都让人头大~ 那有没有一劳永逸的办法呢,让我们不用每次都创建一个新的代理类?那就是我们下面要说到的动态代理了.

动态代理:就是我们不事先为每个原始类编写代理类,而是在运行的时候,动态地创建原始类对应的代理类,然后在系统中用代理类替换掉原始类。那如何实现动态代理呢?
在java中使用是很方便的,系统已经给我们提供了Api,下面就让我们看下如何使用吧。

    interface IRequestInterface {
        fun request(url: String)
    }

    class LoginRequest : IRequestInterface {
        override fun request(url: String) {
             //  ...省略发起登陆请求的代码...
        }
    }

    
    //创建代理类的handler
    class InvokeHandler(private val instance: IRequestInterface, val url: String) : InvocationHandler {
    
        private var startTime by Delegates.notNull()
        private var endTime by Delegates.notNull()
    
        override fun invoke(proxy: Any, method: Method, args: Array?): Any? {

            if (method.name.equals("request")) {
                startTime = System.currentTimeMillis()
            }
    
            //这里需要注意 invoke接收的是可变参数,这里不可以直接传递args,要变成可变参数
            val result = method.invoke(instance, *(args ?: emptyArray()))
    
            when (method.name) {
                "request" -> {
                    endTime = System.currentTimeMillis()
                    report(startTime, endTime, url) 
                }
            }
    
            return result
        }
    
        private fun report(startTime: Long, endTime: Long, url:String) {
            ReportService.report(startTime,endTime,url)
        }
    }

    //使用代码
    class Test {
    companion object {
        @JvmStatic
        fun main(args: Array) {
            val proxy = createProxy(LoginRequest(),"urlLogin")
            proxy.request("urlLogin")
        }

        private fun createProxy(instance: IRequestInterface,url: String): IRequestInterface {
            val classLoader = instance::class.java.classLoader
            val interfaces = instance::class.java.interfaces
            val invokeHandler = InvokeHandler(instance,url)
            return Proxy.newProxyInstance(
                classLoader,
                interfaces,
                invokeHandler
            ) as IRequestInterface
        }
    }
}

我们可以发现,我们把请求耗时、上报的功能都放到了InvokeHandler类的invoke方法中去处理,后续如果我们新增接口,也只需通过Proxy.newProxyInstance动态创建一个代理类就可以了,而无需我们在项目中手动创建xxxProxy类了

你可能感兴趣的:(代理模式)