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

举报
浮生闲半日 发表于 2021/11/12 10:17:56 2021/11/12
【摘要】 今天继续一周两个设计模式系列,这周以结构性模型为主,今天介绍的是结构性模型的代理模式定义:是通过代理对象访问目标对象,这样可以在目标对象基础上增强额外的功能,如添加权限,访问控制和审计等功能。 现实中例子就是中介,例如:购买车票不一定去车站,这个时候代理就出来了,12306等等软件和机构代理模式的主要优点有:代理模式在客户端与目标对象之间起到一个中介作用和保护目标对象的作用;代理对象可以扩展...

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

定义:

是通过代理对象访问目标对象,这样可以在目标对象基础上增强额外的功能,如添加权限,访问控制和审计等功能。 现实中例子就是中介,例如:购买车票不一定去车站,这个时候代理就出来了,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 动态代理要求被代理类实现某个接口
【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。