再聊代理模式前,我們先看下什么場(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)了