一周两个设计模式—设计模式之代理模式(第二周)

935 阅读4分钟

上周顺利度过了,今天继续一周两个设计模式系列,这周以结构性模型为主,今天介绍的是结构性模型的代理模式

定义:

是通过代理对象访问目标对象,这样可以在目标对象基础上增强额外的功能,如添加权限,访问控制和审计等功能。 现实中例子就是中介,例如:购买车票不一定去车站,这个时候代理就出来了,12306等等软件和机构

代理模式的主要优点有:

代理模式在客户端与目标对象之间起到一个中介作用和保护目标对象的作用;
代理对象可以扩展目标对象的功能;
代理模式能将客户端与目标对象分离,在一定程度上降低了系统的耦合度,增加了程序的可扩展性

代理模式分为两种:静态代理模式、动态代理模式,其中动态代理模式分为JDK实现和CGLIB实现。

静态代理模式

代理模式的结构:

抽象主题(Subject)类:通过接口或抽象类声明真实主题和代理对象实现的业务方法。
真实主题(Real Subject)类:实现了抽象主题中的具体业务,是代理对象所代表的真实对象,是最终要引用的对象。
代理(Proxy)类:提供了与真实主题相同的接口,其内部含有对真实主题的引用,它可以访问、控制或扩展真实主题的功能。

下面使用代码展示静态代理模式

//抽象主题接口,主要是保存User的信息
interface IUserDao {
    fun save()
}

具体实现类

class UserDao:IUserDao {
    override fun save() {
       Log.v("======","真实的操作")
    }
}

代理类

class ProxyUser:IUserDao {

    private lateinit var userDao:UserDao

    override fun save() {
        if(!this::userDao.isInitialized){
            userDao = UserDao()
        }
        saveBefore()
        userDao.save()
        saveAfter()
    }

    private fun saveBefore(){
        Log.v("======","操作之前")
    }

    private fun saveAfter(){
        Log.v("======","操作之后")
    }
}

使用方式:

var proxyUser = ProxyUser()
proxyUser.save()

优点:

可以做到在符合开闭原则的情况下对目标对象进行功能扩展

以上就是简单的静态代理方法,下面我们来看看其缺点:

1、 当需要代理多个类的时候,由于代理对象要实现与目标对象一致的接口,有两种方式:

    只维护一个代理类,由这个代理类实现多个接口,但是这样就导致代理类过于庞大
    新建多个代理类,每个目标对象对应一个代理类,但是这样会产生过多的代理类

2、 当接口需要增加、删除、修改方法的时候,目标对象与代理类都要同时修改,不易维护。

为了解决静态代理的缺点,所以就扩展出来了动态代理方法

动态代理模式

定义:

之前我们了解到静态代理指代理类在程序运行前就已经存在。那么反之动态代理的代理类在的程序运行前是不存在的,
也就是说代理类在程序运行时才创建的代理模式成为动态代理。这种情况下,代理类并不是在Java代码中定义好的,
而是在程序运行时根据我们的在Java代码中的“指示”动态生成的
动态代理就是想办法,根据接口或目标对象,计算出代理类的字节码,然后再加载到JVM中使用。
为了让生成的代理类与目标对象(真实主题角色)保持一致性,从现在开始将介绍以下两种最常见的方式:

    通过实现接口的方式 -> JDK动态代理
    通过继承类的方式 -> CGLIB动态代理

下面让我们来看看动态设计模式的实现

除了上面的User类我们在新建一个房产中介的类,当然主要就是买房子了。

public interface BuyHouse {
    void buyHosue();

    void buyEnd();
}

具体实现类:

public class BuyHouseImpl implements BuyHouse {

    @Override
    public void buyHosue() {

        Log.v("=========","=======我要买房");
//        System.out.println("我要买房");
    }

    @Override
    public void buyEnd() {
        Log.v("=========","=======买完了");
    }
}

注意了下面是动态代理了

class DynamicProxy(any:Any):InvocationHandler {

    private var any:Any ?= null

    init {
        this.any = any
    }

    override fun invoke(proxy: Any?, method: Method?, args: Array<out Any>?): Any? {
        Log.v("===========","保存前")
        return method!!.invoke(any,*(args ?: arrayOfNulls<Any>(0)))
    }


}

使用方式:

val userDao: IUserDao = UserDao()

val proxyUser = Proxy.newProxyInstance(
    IUserDao::class.java.classLoader, UserDao::class.java.interfaces,
    DynamicProxy(userDao)
) as IUserDao
proxyUser.save()


val buyHouseImpl: BuyHouse = BuyHouseImpl()

val proxyBuy = Proxy.newProxyInstance(
    BuyHouse::class.java.classLoader, BuyHouseImpl::class.java.interfaces,
    DynamicProxy(buyHouseImpl)
) as BuyHouse
proxyBuy.buyHosue()
proxyBuy.buyEnd()

这样是不是只需要抽象接口和具体实现就可以了,不需要单独的写代理类了,而是通过反射的方式获取需要的方法进行操作。

注意Proxy.newProxyInstance()方法接受三个参数:

    ClassLoader loader:指定当前目标对象使用的类加载器,获取加载器的方法是固定的
    Class<?>[] interfaces:指定目标对象实现的接口的类型,使用泛型方式确认类型
    InvocationHandler:指定动态处理器,执行目标对象的方法时,会触发事件处理器的方法

JDK 动态代理要求被代理类实现某个接口