使用代理模式实现统一标准接口 - 图1



**

本文章是一篇在真实业务场景中使用代理模式,面对业务场景如何使用代理模式去解决问题,主要是实战思路,所以对于代理模式的概念只会简单讲一下。

**

另外如果你觉得本文对你有用的话,请点个在看并且转发一下,只有得到支持才能写更好的文章。

很多人说学了设计模式用不来,唉,这不就跟我当初一样的嘛,设计模式各种文章,各种教程都看完了,我以为自己很厉害,结果在项目中还是TMD用不来,一句话,学了跟没学差不多。
后来为什么我会在项目中使用设计模式呢?就是必须在写代码的时候要思考这里能不能用设计模式,用哪种?这就是我的经验,一旦用熟了,自然而然就能想到,就不用思考了。

如果你已经了解了代理模式的概念,可以直接从我的业务场景开始看。

1.什么是代理模式

为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。

举个例子:Windows 里面的快捷方式。

在程序里,快捷方式就是一个对象,他应该没有行为啊,但是为什么能通过快捷方式打开软件?
真正能打开的应该是exe程序,所以可以看作是代理了exe程序,可以调用exe的行为。

优缺点:

1、代理模式能将代理对象与真实被调用的目标对象分离。
2、一定程度上降低了系统的耦合度,扩展性好。
3、可以起到保护目标对象的作用。
4、可以对目标对象的功能增强。
当然,代理模式也是有缺点的:
1、代理模式会造成系统设计中类的数量增加。
2、在客户端和目标对象增加一个代理对象,会造成请求处理速度变慢。
3、增加了系统的复杂度。

2.JDK与CGLIB动态代理

1、jdk代理生成的代理类只有一个,因而其编译速度是非常快的;而由于被代理的目标类是动态传入代理类中的,Jdk代理的执行效率相对来说低一点,这也是Jdk代理被称为动态代理的原因;

2、Cglib代理需要为每个目标类生成相应的子类,因而在实际运行过程中,其可能会生成非常多的子类,过多的子类始终不是太好的,因为这影响了虚拟机编译类的效率;但由于在调用过程中,代理类的方法是已经静态编译生成了的,因而Cglib代理的执行效率相对来说高一些。

3.我使用代理模式的业务场景

之前做项目需要对接监控视频平台,就是摄像头厂商会有一个开放给用户的api平台,供用户对接,以完成业务需要。例如我要获取某个监控视频的直播地址,获取监控设备的在线状态,都需要通过对接此api平台完成。
前期很长一段时间只对接了1个平台(每个厂商都有自己的平台),后来加入了另一个平台。
为什么会这样呢?因为我们的企业用户的监控设备来自于多个厂商,就需要对接多个厂商的API。
但是在监控设备管理前端,企业用户并不关心此监控设备是来自于哪个厂商,他只需要能看直播,完成指令下发就行了(例如控制监控设备旋转,抓拍图片,录像等等)

  • 4.整理需求

    基于以上的场景,我们的开发人员会面临以下问题:
    不同的厂商的api都不一样,接口请求地址和参数都不一样。
    例如我获取监控设备列表,需要获取直播地址;我在数据库中查询出来设备列表,难道我要循环,然后判断这个监控设备属于哪个厂商的,再调用对应厂商的接口吗。
    这还只是一个接口,当初我对接一个厂商的也有几十个接口,难道开发人员每次调用都需要写if eles吗?

    5.实现目标

    所以为了减轻开发人员的负担,我打算这样做:
    所有操作统一API,将API层抽象出来,每个平台去实现API抽象接口,开发人员只需要注入接口,自动根据参数寻找对应的实现类。
    使用代理模式实现统一标准接口 - 图2
    对,你没看错,开发人员只需要@Autowired 接口
    例如:获取直播地址,开发人员只需要关注接口就行了
    使用代理模式实现统一标准接口 - 图3

    6.实现思路

    如果开发人员只关注接口,那么一个接口有多个实现类啊(多个厂商平台就是多个实现类),
    spring接口注入有多个实现类会报错啊,并且我要实现调用抽象API的方法时才知道是哪个实现类啊,也就是动态查找是哪个平台的实现类,所以不可能一开始就知道是找哪个实现类。
    这时候代理模式就派上用场了。
    我们让每个抽象接口注入代理类(动态代理),代理类再去寻找符合条件的实现类,这是第一点
    使用代理模式实现统一标准接口 - 图4
    第二点这么多抽象接口需要为每个抽象接口生成1个代理类,什么时候生成?

代理类注入流程:
使用代理模式实现统一标准接口 - 图5
代理类生成部分截图使用代理模式实现统一标准接口 - 图6
第三点代理类怎么去寻找到符合条件的实现类,我们可以自定义1个平台注解,在实现类上打上注解,调用抽象API层的时候将平台标识传递过来就可以了啊,生成代理类的时候我们已经将注解和实现类建立了映射关系
使用代理模式实现统一标准接口 - 图7

第四点因为我们是动态代理,spring启动的时候如果用@Autowired 注解还是会报错,因为代理类是动态生成的,spring一开始不知道,所以我们需要使用@lazy注解进行延迟注入
使用代理模式实现统一标准接口 - 图8
第五点 每个厂商的识别监控设备唯一可能不一样,比如有可能是一个参数或者2个参数才能识别到唯一的监控设备进行操作,这时候我们只需要定义1个抽象的父类就行了
使用代理模式实现统一标准接口 - 图9