设计模式之代理模式

·  阅读 363

代理模式.png

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

欢迎来到今天的学习,今天我们一起来学习下使用频率极高的代理模式。多唠叨几句,本月将会对java的设计模式精讲,欢迎点击头像,关注我的专栏,我会持续更新,加油!

系列文章:

设计模式之单例模式

设计模式之工厂模式

设计模式之建造者模式

...持续更新中

话不多说,进入正题

代理模式

代理模式(Proxy Pattern)也叫委托模式,是一个使用率非常高的模式。当你在代码中看到Proxy名称,那肯定是用到了代理模式

顾名思义,就是这个从中间做了下代理,只是个“中间人”,我们生活当中的例子,比如你租房,买房,肯定不是直接跟房东或者跟房产来直接谈。那么“中介”这个职位应运而生。代理模式便是如此。

我们经常用的vpn,便是代理模式的经典案例(俗称翻墙),服务器没有外网访问,没有开辟外网端口,都是在该服务器当中安装vpn软件,外网通过vpn软件登录,方能访问。

我个人非常喜欢的一句话:任何功能与需求没有实现不了的,如果有,那就加一层,这个层便是类似于代理的意思。层层封装,层层代理。需求与条件可以轻松实现

代理模式是一项基本的设计技巧,许多其他的模式,如状态模式、策略模式、访问者模式本质上也采用了代理模式。个人认为这是基础。

image.png

上图中我们看到ProxySubject它分别指向了Subject和RealSubject。

那可以得出:代理模式一般有三种角色:

  • 抽象(Subject)角色,该角色是真实主题和代理主题的共同接口,以便在任何可以使用真实主题的地方都可以使用代理主题。

  • 代理(Proxy Subject)角色,也叫做委托类、代理类,该角色负责控制对真实主题的引用,负责在需要的时候创建或删除真实主题对象,并且在真实主题角色处理完毕前后做预处理和善后处理工作。

  • 真实(Real Subject)角色:该角色也叫做被委托角色、被代理角色,是业务逻辑的具体执行者。

下面我们通过代码深入理解下

代码展示

public interface Subject{
    //定义一个请求
    public void request();
}
复制代码

真实角色

//真实角色
public Class RealSubject implements Subject{
    @Override
    public void request(){
        //输出逻辑,具体业务的执行者
    }
}
复制代码

代理主题ProxySubject的代码如下所示。

//代理类
public class ProxySubject implements Subject {
       private subject subject;

       public ProxySubject (Subject subject) {
           this.subject = subject;
       } 
       
       //实现请求方法 
       public void request() {
            this.beforeRequest();
            subject.request();
            this.afterRequest();
       }
        
       //请求前处理 
       private void beforeRequest(){
       
       }
       //请求后处理
       private void afterRequest(){
       
       }      
}
复制代码

一个代理主题类可以代理多个真实主题,具体代理哪个真实主题是由高层的应用模块决定的,可以通过代理类的构造函数传递被代理者。

为什么要使用代理模式

看完上面的小伙伴可能有同学会问,为什么要使用代理模式。因为感觉没有太大用处,非也。

其实主要有两个比较重要的原因。

  1. 客户端有时无法直接操作某些对象,比如,在分布式应用中,你需要调用的对象可能是运行在另外一台服务器上的,当你访问它时,就必须要通过网络才能访问。如果你让客户端直接去调用,那么就意味着客户端需要处理网络服务,这时就要用我们的代理模式,在客户端和远程服务端之间建立一个网络代理对象,那么客户端只需要调用代理对象就能跟远程对象建立联系,甚至就像调用本地对象一样。这其实就是我们常说的 RPC 服务的基本原理,本质上就是代理模式。

  2. 服务端需要控制客户端的访问权限。 代理模式除了前面提到的扩展功能外,另一个更为重要的功能是做权限控制。比如,某一项业务由于安全原因只能让一部分特定的用户去访问,如果在原有功能的基础上再增加权限过滤功能就会增加代码的耦合性,并且也不方便组件的复用。其实,这时做一个代理类就可以解决该问题,对于特定的接口来说,只需要指定所有请求必须通过该代理类,然后由该代理类做权限判断即可。

分类以及应用场景

  • 第一类,虚拟代理,适用于延迟初始化,用小对象表示大对象的场景:在 Java 中的 CopyOnWriteArrayList 数组对象的实现就是使用了虚拟代理的方式,目的就是要让操作延迟,只有对象被真正用到的时候才会被克隆。

  • 第二类,保护代理,适用于服务端对客户端的访问控制场景,上面提到的权限控制,比如,防火墙其实就是一种保护代理的具体实践。

  • 第三类,远程代理,适用于需要本地执行远程服务代码的场景,比如远程代理框架,比如,gRpc、Dubbo 等。

  • 第四类,日志记录代理,适用于需要保存请求对象历史记录的场景,该场景我认为是在我们实际运用当中最多的一类,利用aop切面,将bean加载到spring当中,比如,日志监控。客户端在调用请求时,并不会感知到日志记录,这是因为代理对象在原始对象周围添加了监控功能。

  • 第五类,缓存代理,适用于缓存客户请求结果并对缓存生命周期进行管理的场景。比如,商品详情页通常包含大量图片和文字介绍,代理对象可以对重复请求相同的结果进行缓存。

总结

这里我们总结一下代理模式试图解决的问题:

  • 无法直接调用某些对象;

  • 某个接口可能需要外部额外操作,如日志记录、权限管控、重复操作等;

  • 需要控制访问权限。

弦外之音

OK 今天的代理模式就讲到这里,感谢你的阅读,如果你感觉学到了东西,麻烦您点赞,关注。

我已经将本章收录在专题里,点击下方专题,关注专栏,我会每天发表干货,本月我会持续输入设计模式。

加油! 我们下期再见

分类:
后端
标签: