代理模式

再聊代理模式前,我們先看下什么場(chǎng)景下我們會(huì)需要用到代理模式,以及為什么要用?

舉個(gè)我們代碼中常見(jiàn)的例子:
產(chǎn)品需求:我們需要看到一些指定接口的請(qǐng)求數(shù)據(jù),比如請(qǐng)求總耗時(shí)時(shí)間,上報(bào)給服務(wù)器,用于了解用戶(hù)的使用體驗(yàn)。

那我們開(kāi)始擼代碼了

    class LoginRequest {
        fun login() {

            val startTime = System.currentTimeMillis()

            //  ...省略發(fā)起登陸請(qǐng)求的代碼...

            val endTime = System.currentTimeMillis()

            //上報(bào)接口請(qǐng)求的開(kāi)始時(shí)間和結(jié)束時(shí)間給服務(wù)器
            ReportService.report(startTime,endTime,"urlLogin")
        }
    }


    class HomePageRequest {
        fun homePageData() {

            val startTime = System.currentTimeMillis()

            //  ...省略發(fā)起首頁(yè)數(shù)據(jù)請(qǐng)求的代碼...

            val endTime = System.currentTimeMillis()

            //上報(bào)接口請(qǐng)求的開(kāi)始時(shí)間和結(jié)束時(shí)間給服務(wù)器
            ReportService.report(startTime,endTime,"urlHomePage")
        }
    }

很明顯,上面的寫(xiě)法有2個(gè)問(wèn)題,
1:請(qǐng)求耗時(shí)的代碼和上報(bào)日志的代碼完全重復(fù),我們需要在每一個(gè)上報(bào)的請(qǐng)求中都重復(fù)此代碼.
2:獲取請(qǐng)求耗時(shí)的代碼和上報(bào)的代碼,和請(qǐng)求的代碼在業(yè)務(wù)上毫無(wú)關(guān)聯(lián),耦合太高.

為了將上報(bào)框架代碼和請(qǐng)求業(yè)務(wù)代碼解耦,代理模式就派上用場(chǎng)了!

簡(jiǎn)單說(shuō)下什么是代理模式:代理模式的本質(zhì)就是在不改變?cè)碱?lèi)(或叫被代理類(lèi))代碼的情況下,通過(guò)引入代理類(lèi)來(lái)給原始類(lèi)附加功能。了解了代理模式的本質(zhì),我們?cè)賹?xiě)改造一下上面的例子也加深理解。

    interface ILoginInterface{
        fun login()
    }

    class LoginRequest : ILoginInterface{
        override fun login() {
            //  ...省略發(fā)起登陸請(qǐng)求的代碼...
        }
    }
    
    class LoginRequestProxy(private val realLogin:LoginRequest):ILoginInterface{
        override fun login() {
            val startTime = System.currentTimeMillis()

            realLogin.login()

            val endTime = System.currentTimeMillis()

            //上報(bào)接口請(qǐng)求的開(kāi)始時(shí)間和結(jié)束時(shí)間給服務(wù)器
            ReportService.report(startTime,endTime,"urlLogin")
        }
    }

通過(guò)代碼我們可以發(fā)現(xiàn),LoginRequest類(lèi)中只有登陸請(qǐng)求的業(yè)務(wù)代碼了,獲取請(qǐng)求耗時(shí)和上報(bào)的代碼都被我們放置在了LoginRequestProxy中了,這樣我們就做到了上報(bào)框架代碼和業(yè)務(wù)代碼的解耦。這種寫(xiě)法就是靜態(tài)代理.

但上面的寫(xiě)法也有個(gè)問(wèn)題在于:我們需要給每個(gè)需要解耦、需要被代理的類(lèi)都去創(chuàng)建一個(gè)xxxProxy代理的類(lèi),這個(gè)工作不僅繁瑣,同時(shí)還增加了維護(hù)成本,后續(xù)只要我們新增一個(gè)接口請(qǐng)求,都要?jiǎng)?chuàng)建一個(gè)代理類(lèi),實(shí)現(xiàn)相同的接口。這聽(tīng)著都讓人頭大~ 那有沒(méi)有一勞永逸的辦法呢,讓我們不用每次都創(chuàng)建一個(gè)新的代理類(lèi)?那就是我們下面要說(shuō)到的動(dòng)態(tài)代理了.

動(dòng)態(tài)代理:就是我們不事先為每個(gè)原始類(lèi)編寫(xiě)代理類(lèi),而是在運(yùn)行的時(shí)候,動(dòng)態(tài)地創(chuàng)建原始類(lèi)對(duì)應(yīng)的代理類(lèi),然后在系統(tǒng)中用代理類(lèi)替換掉原始類(lèi)。那如何實(shí)現(xiàn)動(dòng)態(tài)代理呢?
在java中使用是很方便的,系統(tǒng)已經(jīng)給我們提供了Api,下面就讓我們看下如何使用吧。

    interface IRequestInterface {
        fun request(url: String)
    }

    class LoginRequest : IRequestInterface {
        override fun request(url: String) {
             //  ...省略發(fā)起登陸請(qǐng)求的代碼...
        }
    }

    
    //創(chuàng)建代理類(lèi)的handler
    class InvokeHandler(private val instance: IRequestInterface, val url: String) : InvocationHandler {
    
        private var startTime by Delegates.notNull<Long>()
        private var endTime by Delegates.notNull<Long>()
    
        override fun invoke(proxy: Any, method: Method, args: Array<out Any>?): Any? {

            if (method.name.equals("request")) {
                startTime = System.currentTimeMillis()
            }
    
            //這里需要注意 invoke接收的是可變參數(shù),這里不可以直接傳遞args,要變成可變參數(shù)
            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<String>) {
            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
        }
    }
}

我們可以發(fā)現(xiàn),我們把請(qǐng)求耗時(shí)、上報(bào)的功能都放到了InvokeHandler類(lèi)的invoke方法中去處理,后續(xù)如果我們新增接口,也只需通過(guò)Proxy.newProxyInstance動(dòng)態(tài)創(chuàng)建一個(gè)代理類(lèi)就可以了,而無(wú)需我們?cè)陧?xiàng)目中手動(dòng)創(chuàng)建xxxProxy類(lèi)了

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀(guān)點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容