设计模式是什么鬼(代理)

简介: 设计模式是什么鬼(代理)

代理,代表打理,以他人的名义代表委托人打理其本职工作之外或不所能及的事务,达成合作关系并更高效地促成事务完成的目的。例如明星经纪人,他们并没有像明星一样会唱歌、跳舞或演戏,而是替明星打理一些无暇顾及的事务(这并不代表可以代理分外之事image.gif),比如推广与宣传,合同谈判啊之类,达成和约后他们才会通知明星去表演。再比如机票销售代理商既不造飞机也不提供乘机服务,他们只负责卖票,代理律师并不会因胜诉获得赔偿金或者败诉受到法律制裁,他们只负责代理打官司,等等等等。


image.png


生活中还有很多其他实例不胜枚举,但对于做技术的我们,以网络代理举例相信是最合适不过了。


image.png


首先,我们上网前得去网络服务提供商(ISP)申请互联网宽带业务,于是顺理成章光纤入户,并拿到一个调制解调器,也就是我们俗称的“猫”。好,“猫”实现了互联网访问接口,看代码。


1public interface Internet {//互联网访问接口
2
3    public void access(String url);
4
5}


1public class Modem implements Internet {//调制解调器
2
3    @Override
4    public void access(String url){//实现互联网访问接口
5        System.out.println("正在访问:" + url);
6    }
7}


作为调制解调器,一定有上网功能了,用户的电脑只需要用网线连接这只“猫”便接入互联网了。就这么简单么?然而某天我们发现孩子学习时总是偷偷上网看电影玩游戏,于是我们决定对某些网站进行过滤,拒绝黄赌毒侵害未成年。那么,我们需要在客户终端电脑与猫之间加一层代理,用于过滤某些不良网站,最终我们决定购买一款有过滤功能的路由器。


1public class RouterProxy implements Internet {//路由器代理类
 2
 3    private Internet modem;//持有被代理类引用
 4    private List<String> blackList = Arrays.asList("电影", "游戏", "音乐", "小说");
 5
 6    public RouterProxy() {
 7        this.modem = new Modem();//实例化被代理类
 8        System.out.println("拨号上网...连接成功!");
 9    }
10
11    @Override
12    public void access(String url) {//同样实现互联网访问接口方法
13        for (String keyword : blackList) {//循环黑名单
14            if (url.contains(keyword)) {//是否包含黑名单字眼
15                System.out.println("禁止访问:" + url);
16                return;
17            }
18        }
19        modem.access(url);//正常访问互联网
20    }
21}


注意看,在这里路由器代理主要充当代理的角色,和之前的“猫”一样,它同样实现了互联网接口,看似也是有上网功能的,其实不然。第12行代码对于互联网访问功能的实现一开始就做了个过滤,如果地址中带有黑名单中的敏感字眼则禁止访问并直接退出,反之则于第19行调用“猫”的互联网访问方法,看到了吧,最终还是调用“猫”的上网功能。注意此处为了对“猫”进行控制,代理专为此而生,我们直接于第7行实例化它而不是需要别人把它注入进来。好了,孩子现在来上网了,迫不及待运行之。


1public class Client {
 2    public static void main(String[] args) {
 3        Internet proxy = new RouterProxy();//实例化的是代理
 4        proxy.access("http://www.电影.com");
 5        proxy.access("http://www.游戏.com");
 6        proxy.access("ftp://www.学习.com/java");
 7        proxy.access("http://www.工作.com");
 8
 9        /* 运行结果
10            拨号上网...连接成功!
11            禁止访问:http://www.电影.com
12            禁止访问:http://www.游戏.com
13            正在访问:ftp://www.学习.com/java
14            正在访问:http://www.工作.com
15        */
16    }
17}


第3行处,孩子实例化的不再是“猫”,而是被偷梁换柱的替换为路由器代理了,也就是说大家上网都连接路由器了,而不是直接去连“猫”,这样不但省去了我们拨号的麻烦(路由器帮助拨号)而且孩子再也访问不到乱七八糟的网站了。而这个代理自身其实并不具备访问互联网的能力,它只是简单的调用“猫”上网功能,其存在目的只是为了控制对”猫“的互联访问,对其进行代理而已。


说到这里大家有没有发现这个代理模式是不是与装饰器模式很类似?如果观察UML类图关系你会发现几乎一模一样,那这个模式存在的意义何在?其实,代理模式更强调的是对被代理对象的控制,而不是仅限于去装饰目标对象并增强其原有的功能。就像明星的例子一样,如果钱没给够,合同未达成,则不让明星随意作秀。


相信大家已经理解地很通透了吧,这也是我们最常用的代理模式了。其实还有一种叫动态代理,不同之处在于其实例化过程是在运行时完成的,也就是说我们不需要专门针对某个接口去写这么一个代理类,而是根据接口动态生成。


举个例子,让我们先忘掉之前的路由器代理,当我们内网中的上网设备越来越多,路由器的Lan口已被占满不够用了,于是我们决定换成交换机,看代码。


1public interface Intranet {//局域网访问接口
2
3    public void fileAccess(String path);
4
5}


为了保持简单,我们假设这个交换机Switch实现了局域网访问接口Intranet,请注意这里是互联网接口Internet。


1public class Switch implements Intranet {
2
3    @Override
4    public void fileAccess(String path){
5        System.out.println("访问内网:" + path);
6    }
7
8}


这里进行的是局域网文件访问,比如说是拷贝另一台内网机器上的共享文件,并且我们想保证与之前一样的关键字过滤控制功能,也就是说不管是什么地址都要先通过过滤,怎么复用呢?


到这里让我们思考一下,猫实现的是互联网访问接口,交换机实现的是局域网访问接口,那我们的过滤器代理类到底该怎么写?是实现Internet接口呢还是实现Intranet接口呢?要么两个都实现?再加进来新的类接口又要不停地改实现类吗?这显然行不通,过滤器无非就是一段过滤逻辑不必来回改动,这违反了设计模式开闭原则。动态代理应时而生,我们来看代码。


1public class KeywordFilter implements InvocationHandler {
 2
 3    private List<String> blackList = Arrays.asList("电影", "游戏", "音乐", "小说");
 4
 5    // 被代理的真实对象,猫、交换机、或是别的什么都是。
 6    private Object origin;
 7
 8    public KeywordFilter(Object origin) {
 9        this.origin = origin;//注入被代理对象
10        System.out.println("开启关键字过滤模式...");
11    }
12
13    @Override
14    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
15        //要被切入方法面之前的业务逻辑
16        String arg = args[0].toString();
17        for (String keyword : blackList) {
18            if (arg.toString().contains(keyword)) {
19                System.out.println("禁止访问:" + arg);
20                return null;
21            }
22        }
23        //调用真实的被代理对象方法
24        return method.invoke(origin, arg);
25    }
26
27}


对于这个关键字过滤功能我们不再写到代理类里面了,而是另外写个类并实现JDK反射包中提供的InvocationHandler接口,于第9行注入即将被代理的对象,不管是猫还是交换机什么的它总归是个Object,然后在第14行实现这个invoke调用方法,之后生成的动态代理将来会调进来跑这块的逻辑,很显然我们这里依然保持不变的逻辑,在真实对象方法被执行之前运行了过滤逻辑加以控制。由于传入的参数是被代理对象的方法method,以及一堆参数args,所以注意这里第24行我们要用反射去调用被代理对象origin了,最后来看我们如何运行。


1public class Client {
 2    public static void main(String[] args) {
 3
 4        //访问外网(互联网),生成猫代理。
 5        Internet internet = (Internet) Proxy.newProxyInstance(
 6                Modem.class.getClassLoader(),
 7                Modem.class.getInterfaces(), 
 8                new KeywordFilter(new Modem()));
 9        internet.access("http://www.电影.com");
10        internet.access("http://www.游戏.com");
11        internet.access("http://www.学习.com");
12        internet.access("http://www.工作.com");
13
14        //访问内网(局域网),生成交换机代理。
15        Intranet intranet = (Intranet) Proxy.newProxyInstance(
16                Switch.class.getClassLoader(),
17                Switch.class.getInterfaces(), 
18                new KeywordFilter(new Switch()));
19        intranet.fileAccess("\\\\192.68.1.2\\共享\\电影\\IronHuman.mp4");
20        intranet.fileAccess("\\\\192.68.1.2\\共享\\游戏\\Hero.exe");
21        intranet.fileAccess("\\\\192.68.1.4\\shared\\Java学习资料.zip");
22        intranet.fileAccess("\\\\192.68.1.6\\Java知音\\设计模式是什么鬼.doc");
23
24        /*
25            开启关键字过滤模式...
26            禁止访问:http://www.电影.com
27            禁止访问:http://www.游戏.com
28            正在访问:http://www.学习.com
29            正在访问:http://www.工作.com
30            开启关键字过滤模式...
31            禁止访问:\\192.68.1.2\共享\电影\IronHuman.mp4
32            禁止访问:\\192.68.1.2\共享\游戏\Hero.exe
33            访问内网:\\192.68.1.4\shared\Java学习资料.zip
34            访问内网:\\192.68.1.6\Java知音\设计模式是什么鬼.doc
35
36        */
37    }
38}


可以看到,我们不管是访问互联网还是局域网,只需要分别生成相应的代理并调用即可,相同的过滤器逻辑被执行了。如此一来,我们并不需要再写任何的代理类了,只需要实现一次InvocationHandler就一劳永逸了,在运行时去动态地生成代理,达到兼容任何接口的目的。


其实在很多框架中大量应用到了动态代理模式,比如Spring的面向切面AOP,我们只需要定义好一个切面类@Aspect,声明其切入点@Pointcut(被代理的哪些对象的哪些方法,也就是这里的猫和交换机的access以及accessFile),以及被切入的代码块(要增加上去的逻辑,比如这里的过滤功能代码,可分为前置执行@Before,后置执行@After,以及异常处理@AfterThrowing等),于是框架自动帮我们生成代理并切入目标执行。正如给每给方法前后加入日志的例子,或者更经典的事务控制的例子,在所有业务代码之前先切入“事务开始”,执行过后再切入“事务提交”,如果抛异常被捕获则执行“事务回滚”,如此就不必要在每个业务类中去写这些重复代码了,一劳永逸,冗余代码大量减少,开发效率惊人提升。



image.png



两耳不闻窗外事,一心只读圣贤书,毕竟隔行如隔山,山外之事还是交给专家去代理吧。

相关文章
|
4月前
|
设计模式 XML JSON
二十三种设计模式全面解析-代理模式进阶篇:揭秘远程代理
二十三种设计模式全面解析-代理模式进阶篇:揭秘远程代理
|
7月前
|
设计模式 缓存 安全
设计模式之代理模式的懂静态代理和动态代理
设计模式之代理模式的懂静态代理和动态代理
62 0
|
2月前
|
设计模式 缓存 安全
设计模式-代理模式(静态代理、动态代理、cglib代理)、代理模式和装饰者模式的区别
设计模式-代理模式(静态代理、动态代理、cglib代理)、代理模式和装饰者模式的区别
55 1
|
3月前
|
设计模式
装饰者设计模式(二)番外篇 装饰者设计模式和静态代理设计模式区别
装饰者设计模式(二)番外篇 装饰者设计模式和静态代理设计模式区别
|
5月前
|
设计模式 Java
设计模式之代理模式(静态&动态)代理
二十三种设计模式中的一种,属于结构型模式。它的作用就是通过提供一个代理类,让我们在调用目标方法的时候,不再是直接对目标方法进行调用,而是通过代理类间接调用。让不属于目标方法核心逻辑的代码从目标方法中剥离出来——解耦。调用目标方法时先调用代理对象的方法,减少对目标方法的调用和打扰,同时让附加功能能够集中在一起也有利于统一维护。
24 0
|
11月前
|
设计模式 大数据
大数据开发基础的设计模式的代理
代理模式是大数据开发基础的设计模式之一。它是一种结构型模式,用于为其他对象提供一种代理以控制对这个对象的访问。代理模式可以在不改变原始类代码的情况下,对原始类进行扩展或增强。
60 0
|
12月前
|
设计模式 缓存 前端开发
前端通用编程基础的设计模式之代理
代理模式是一种常见的设计模式,它可以帮助我们在不改变原始代码的情况下增加新的功能,并且可以实现对对象的控制。下面就让我们来看看代理模式的特点和优势。
54 0
|
设计模式 Java
设计模式-代理设计模式
代理模式(Proxy) 代理模式的基本介绍 1、代理模式:为一个对象提供一个替身,以控制对这个对象的访问。即通过代理对象访问目标对象,这样做的好处是,可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能。 2、代理模式有不同的形式,主要有三种,静态代理、jdk代理(也叫接口代理)、cglib代理(可以在内存中动态的创建对象,而不需要实现接口)。
67 0
|
设计模式 数据采集 Java
java设计模式之代理设计模式(Spring核心思想AOP的底层设计模式)
代理设计模式 🍅 作者:程序员小王 🍅 程序员小王的博客:程序员小王的博客 🍅 扫描主页左侧二维码,加我微信 一起学习、一起进步 🍅 欢迎点赞 👍 收藏 ⭐留言 📝 🍅 如有编辑错误联系作者,如果有比较好的文章欢迎分享给我,我会取其精华去其糟粕 🍅java自学的学习路线:java自学的学习路线
94 0
java设计模式之代理设计模式(Spring核心思想AOP的底层设计模式)
|
设计模式 Java
Java设计模式之:静态代理
Java设计模式之:静态代理
68 0