likes
comments
collection
share

大聪明教你学Java设计模式 | 第五篇:代理模式

作者站长头像
站长
· 阅读数 60

前言

这是我参与2022首次更文挑战的第8天,活动详情查看:2022首次更文挑战

大聪明在写代码的过程中发现设计模式的影子是无处不在,设计模式也是软件开发人员在软件开发过程中面临的一般问题的解决方案。大聪明本着“独乐乐不如众乐乐”的宗旨与大家分享一下设计模式的学习心得。

在写代码的时候我们有时候会遇到访问对象不适合或者不能直接引用目标对象的情况,就比如在内网中,出于安全原因需要屏蔽客户端直接访问真实对象等,面对这种情况我们该怎么办呢?开动各位聪明的小脑瓜思考一下下🤔~~

没错,这时候我们就要用到代理模式了。

代理模式

首先先简单说一下什么是代理模式。 代理模式的定义:代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。

这个定义好像不太好理解,那么还是本着“繁琐问题必有猥琐解法”的宗旨,我们来把他的定义描述的再直白一些。 比如买火车票这件事,在以前没有12306APP的时候,很多人都是去联系黄牛去买火车票,黄牛相当于是我们本人的的代理,通过黄牛买票,我们可以避免与火车站的直接交互,可以省很多事,并且还能享受到黄牛更好的服务(当然啦,钞票💴还是要给足的)。

接下来我们就要引入另一个概念了:静态代理和动态代理。 在软件开发中,我们经常把代理模式分为静态代理和动态代理,那么这二者的区别又是什么呢? 静态代理,顾名思义他是静态的(说了句废话😜),它的本质是自己手写(或者用工具生成)代理类,也就是在程序运行前就已经存在的编译好的代理类。但是,如果我们需要很多的代理,每一个都这么去创建那得浪费太多时间了,而且会有大量的重复代码,此时我们就可以选择动态代理,动态代理可以在程序运行期间根据需要动态的创建代理类及其实例来完成具体的功能。

说白了,静态代理和动态代理的本质区别就是代理类的创建时机和创建方式的不同。

老规矩,咱们还是用代码来具体分析一下二者之间的区别。

静态代理

第一步:创建一个服务类接口

/**
 * @description: BuyTicket
 * @author: 庄霸.liziye
 * @create: 2021-08-10 16:11
 **/
public interface BuyTicket {

    void buyTicket();
    
}

第二步:实现服务接口

/**
 * @description: BuyTicketImpl
 * @author: 庄霸.liziye
 * @create: 2021-08-10 16:13
 **/
public class BuyTicketImpl implements BuyTicket{

    @Override
    public void buyTicket() {
        System.out.println("我要买火车票");
    }
}

第三步:创建代理类

/**
 * @description: BuyTicketProxy
 * @author: 庄霸.liziye
 * @create: 2021-08-10 16:14
 **/
public class BuyTicketProxy implements BuyTicket {
    private BuyTicket buyTicket;

    public BuyTicketProxy(final BuyTicket buyTicket) {
        this.buyTicket = buyTicket;
    }

    @Override
    public void buyTicket() {
        System.out.println("拿着钱去买票");
        buyTicket.buyTicket();
        System.out.println("坐上了火车去拉萨");

    }
}

最后我们写一个测试类执行一下

/**
 * @description: test
 * @author: 庄霸.liziye
 * @create: 2021-08-09 15:30
 **/
public class test {
    public static void main(String[] args) throws CloneNotSupportedException {
        BuyTicket buyTicket = new BuyTicketImpl();
        buyTicket.buyTicket();
        BuyTicketProxy buyTicketProxy = new BuyTicketProxy(buyTicket);
        buyTicketProxy.buyTicket();
    }
}

大聪明教你学Java设计模式 | 第五篇:代理模式 我们可以看出静态代理可以做到在符合开闭原则的情况下对目标对象进行功能扩展,不过我们得为每一个服务都得创建代理类,工作量太大,不易管理,而且接口一旦发生改变,代理类也得相应修改。

动态代理

在动态代理中我们不再需要再手动的创建代理类,我们只需要编写一个动态处理器就可以了。真正的代理对象由JDK在运行时为我们动态的来创建。

首先我们创建一个动态处理器

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
 * @description: DynamicProxyHandler
 * @author: 庄霸.liziye
 * @create: 2021-08-10 16:26
 **/
public class DynamicProxyHandler implements InvocationHandler {

    private Object object;

    public DynamicProxyHandler(final Object object) {
        this.object = object;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("拿着钱去买票");
        Object result = method.invoke(object, args);
        System.out.println("坐上了火车去拉萨");
        return result;
    }
}

我们再编写一个测试类

import java.lang.reflect.Proxy;

/**
 * @description: DynamicProxyTest
 * @author: 庄霸.liziye
 * @create: 2021-08-10 16:38
 **/
public class DynamicProxyTest {
    public static void main(String[] args) {
        BuyTicket buyTicket = new BuyTicketImpl();
        BuyTicket proxyBuyTicket = (BuyTicket) Proxy.newProxyInstance(BuyTicket.class.getClassLoader(), new
                Class[]{BuyTicket.class}, new DynamicProxyHandler(buyTicket));
        proxyBuyTicket.buyTicket();
    }
}

这里需要注意的一点是Proxy.newProxyInstance()方法接收三个参数:

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

大聪明教你学Java设计模式 | 第五篇:代理模式 与静态代理相比,我们可以看出动态代理大大减少了我们的开发任务,同时减少了对业务接口的依赖,降低了耦合度。 除此之外,我们还发现代理对象proxyBuyTicket所对应的类继承自java.lang.reflect.Proxy类,这也正是JDK动态代理机制无法实现对class的动态代理的原因:Java只允许单继承。

小结

我们可以自己直接火车站去买票,那么在软件开发中,方法直接调用就可以完成功能,为什么非要通过代理呢?原因是采用代理模式可以有效的将具体的实现(买票过程)与调用方(买票者)进行解耦,通过面向接口进行编码完全将具体的实现(买票过程)隐藏在内部(黄牛)。除此之外,代理类不仅仅是一个隔离客户端和目标类的中介,我们还可以借助代理来在增加一些功能,而不需要修改原有代码,这也是开闭原则的典型应用。

事实上,真正的业务功能还是由目标类来实现,代理类只是用于扩展、增强目标类的行为。就比如,在项目开发中我们在后期需要加入日志功能,这时候就可以使用代理来实现,而没有必要去直接修改已经封装好的目标类。

本人经验有限,有些地方可能讲的没有特别到位,如果您在阅读的时候想到了什么问题,欢迎在评论区留言,我们后续再一一探讨🙇‍

希望各位小伙伴动动自己可爱的小手,来一波点赞+关注 (✿◡‿◡) 让更多小伙伴看到这篇文章~ 蟹蟹呦(●'◡'●)

如果文章中有错误,欢迎大家留言指正;若您有更好、更独到的理解,欢迎您在留言区留下您的宝贵想法。

爱你所爱 行你所行 听从你心 无问东西

转载自:https://juejin.cn/post/7065854536673411086
评论
请登录