设计模式-代理模式

一、前言

​ 代理模式有利于针对某些场景进行解耦。通常有两种实现方式,静态代理和动态代理,这里对其进行记录。除了解耦外,还有一个好处就是让程序结构更加清晰理解

二、静态代理

​ 假设有这么一个场景,我们找房子,通常找房东的话会有很多流程,但是找中介的话只要告诉需求就行了。中介会给我们最终的结果,中介自己把其余繁琐流程进行处理。虽然在现实中找中介也会很麻烦。简要代码如下:

class Subject {
    fun call(){
        println("---->找到房子")
    }
}
class Proxy(private val subject: Subject) {
    fun call(){
        println("-->开始找房,联系房东")
        subject.call()
        println("-->找房结束,进行后续维护")
    }
}

class ExampleUnitTest {
    @Test
    fun proxyTest(){
        val subject = Subject()
        val proxy = Proxy(subject)
        proxy.call()
    }
}

执行结果如下:

-->开始找房,联系房东
---->找到房子
-->找房结束,进行后续维护

我们这里看下如果我们不使用代理模式的话会怎么写。代码如下:

class ExampleUnitTest {  
		@Test
    fun proxyTest(){
        val subject = Subject()
        println("-->开始找房,联系房东")
        subject.call()
        println("-->找房结束,进行后续维护")
    }
}

可以看到这个代码把一些不必要的细节暴漏了出来,虽然可以写个管理类啥的进行二次封装。但是还是没有分割开,不利于重用。这些代码少的话还好,要是多的话就会看起来很凌乱。

三、动态代理

​ 上文记录了静态代理。不过静态代理有个问题,如果有很多个需要进行代理的话,就要写很多个代理类,比如我找房子,找了链家、我爱我家、我爱我屋啥的。这样的话在实际开发上就会很费劲,总不能每次产生一个新的东西,都要写一个代理类吧。虽然也可以写个管理类,类似于中介的中介,一个大管家管理所有中介。不过这个不在这次讨论范围。

​ 这里对上述的代理模式进行改动,由于动态代理需要使用接口(动态代理会生成新的类,所以也只能是接口),所以修改如下:

interface Subject {
    fun call()
}
class SubjectIml: Subject {
    override fun call() {
        println("---->找到房子")
    }
}
import java.lang.reflect.InvocationHandler
import java.lang.reflect.Method
import  java.lang.reflect.Proxy
class ProxyFactory(private val target: Any) {
    //给目标对象生成代理对象
    fun getProxyInstance(): Any? {

        return Proxy.newProxyInstance(
            target.javaClass.classLoader,
            target.javaClass.interfaces,
            object : InvocationHandler {
                @Throws(Throwable::class)
                override operator fun invoke(proxy: Any?, method: Method, args: Array<out Any>?): Any? {
                    println("-->开始找房,联系房东")
                    //执行目标对象方法
                    val returnValue: Any? = method.invoke(target, *(args ?: emptyArray()) )
                  //*(args ?: emptyArray()) 这句代码的*是伸展操作符,将一个数组展开程可变数组。由因为这个数组不可为null,所以需要使用?: 进行判断。
                    println("-->找房结束,进行后续维护")
                    return returnValue //需要注意的是这里返回什么都行,所以在Retrofit里面返回了自己定义的新的对象,而这个对象和传入的对象没有关系
//                    return null
//                    return 1
//                    return ""
                }
            }
        )
    }
}

class ExampleUnitTest {
    @Test
    fun proxyFactory(){
        val subject = SubjectIml()
        val proxy: Subject = ProxyFactory(subject).getProxyInstance() as Subject
        println("-->"+proxy.javaClass)
        proxy.call()
    }
}

四、仿写Retrofit的动态代理

Retrofit的动态代理和这个略有不同,因为Retrofit只有接口,所有具体实现类均由动态代理实现所以代码修改如下:

interface Subject {
    fun call()
}

class ProxyFactory {
    //给目标对象生成代理对象
    fun <T> getProxyInstance(service: Class<T>): Any? {
        return Proxy.newProxyInstance(
            service.classLoader,
            arrayOf(service),
            object : InvocationHandler {
                @Throws(Throwable::class)
                override operator fun invoke(proxy: Any?, method: Method, args: Array<out Any>?): Any? {
                    println("-->开始找房,联系房东")
                    //执行目标对象方法

                    // If the method is a method from Object then defer to normal invocation.
                    if (method.declaringClass == Any::class.java) {
                        return method.invoke(this, *(args ?: emptyArray()))
                    }
                    println("-->找房结束,进行后续维护")
//                    return null
//                    return 1
                    return ""
                }
            }
        )
    }
}

class ExampleUnitTest {
    @Test
    fun proxyFactory(){
        val proxy: Subject = ProxyFactory().getProxyInstance(Subject::class.java) as Subject
        println("-->"+proxy.javaClass)
        proxy.call()
    }
}

需要注意的是因为没有执行自己实现的具体类,所以当初写的那个具体类的代码将不会执行,其结果如下:

-->class com.sun.proxy.$Proxy14
-->开始找房,联系房东
-->找房结束,进行后续维护

五、参考链接

  1. Java的三种代理模式
  2. 函数
  3. kotlin 爬坑指南–kotlin开发记录的问题
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值