设计模式之代理模式
我相信我们的朋友圈都有过微商推广商品的广告,你也一定知道“瓜子二手车直卖网,没有中间商赚差价“。或者是租房的时候那些中介,相亲的时候那些媒婆。他们其实就是一些代理,或者说是一些中介。我们通过代理买东西的过程其实就是代理模式的灵活体现。下面我们就好好的分析一下,设计模式中常见的代理模式。
一、认识代理模式
代理模式分为动态代理和静态代理。两者的差别还是很大的,不过思想都是一样的,起到一个服务中介的作用。
在这里我们通过出租房子为例进行演示代理模式。我们知道我们出租房子一般是通过两种途径,第一,我们直接找到租客进行洽谈。第二,我们还可以通过那些黑心中介来帮助我们出租。但是不管是哪一种方式都是为了出租房子。下面我们就使用一张图来直观的表示一下:
1-代理模式.png
从上图我们能够看出,代理模式其实特别的简单,房屋主人想要出租房屋,现在要通过第二种方式,使用中介来出租。中介就是代理。出租整个流程就是代理模式的应用
思想我想大家都已经能够明白了,下面看一下类图,再从类的角度分析一下代理模式
2-代理模式类图.jpg
从上面这张图我们可以看到,一共有四个角色:
(1)User:相当于用户类,调用代理出租
(2)RealSubject(房屋主人):是真正出租房子的人
(3)Proxy(中介):表面上出租房子的人
(4)subject(抽象对象接口):这里定义了房屋主人想要让中介做的事,出租房子
对代理模式的基本思想有了认识之后,我们就可以好好看看静态代理和动态代理模式是如何实现的了。我们首先看的是静态代理模式。
二、代码实现静态代理模式
静态代理:所谓静态代理也就是在程序运行前就已经存在代理类的字节码文件(在真正出租房屋之前,就已经做好了相关工作,中介和房屋主人就做好了相关联系),代理类和委托类的关系在运行前就确定了。我们先拿出租房子的实例来演示,然后再回来理解就清楚了。
第一步:定义抽象接口(也就是房子主人让中介做的事:出租房子)
//定义了房屋主人想让中介做的事:出租房子
public interface HouseSubject {
public void rentHouse();
}
第二步:定义具体对象(房屋主人)
//房屋主人出租房子
public class RealHouseSubject implements HouseSubject{
@Override
public void rentHouse() {
System.out.println("我是房子主人,我要出租房子");
}
}
第三步:代理(中介)
public class HouseProxy implements HouseSubject{
@Override
public void rentHouse() {
//代理只需要给房屋打广告就好了
this.ad();
//创建一个房屋的主人,让真实的主人出租
RealHouseSubject realSubject = new RealHouseSubject();
realSubject.rentHouse();
//房子卖出去之后,撤销广告
this.backAd();
}
private void ad() {
System.out.println("广告:xxx街道出租房子了");
}
private void backAd() {
System.out.println("房子已经租出去了,现在要把广告撤销");
}
}
第四步:User(演示整个出租过程)
public class User {
public static void main(String[] args) {
HouseProxy proxy = new HouseProxy();
proxy.rentHouse();
}
}
最后看看结果吧,我们运行了User之后会出现
3-结果.png
代理模式就是这么简单,如果还不理解,动手敲一遍代码就清楚了。从上面的代码其实我们会发现,我们的代理其实啥都没有干,就算是出租房子也是直接new出来一个RealHouseSubject。让它去出租的房子。是不是有一种被中介欺骗了的感觉,明明中介没有出租房子,但是表面上还是他出租的房子。这就是静态代理,它存在了大量的弊端,比如说当我们的房屋主人很多,而且每一个房屋主人的要求也不一样,有的房屋主人要求为其出租房子的同时还要为其打扫卫生,还要为其砌墙等等(很明显要求不合理,但是中介还要干),这时候再去用静态代理就会很麻烦。这时候就开始用到动态代理了。下面我们就开始看看动态代理是如何实现的这个例子。
三、代码实现动态代理模式
1、为什么要用到动态代理?
为什么要使用动态代理,当然是静态代理有缺点不能满足我们的要求了,现在把缺点整理一下:
(1)代理对象(中介)的一个接口只服务于一种类型的对象(房屋主人),如果要代理的方法很多,势必要为每一种方法都进行代理,静态代理在程序规模稍大时就无法胜任了。
(2)如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。
有了这些缺点,所以我们开始使用动态代理。首先我们要理解动态代理是什么,文字总是很枯燥,上一张图来看一下他的思想吧。
5-动态代理.png
从上图我们可以看到,动态代理可以服务多个目标对象。当目标对象的需求不一定,而且很多时候也能够很好的去完成工作。也就是在静态代理中提前做好一些事,但是动态代理不是,动态代理在真正需要去做的时候再去做,而且是把相应的工作拿过来(比如说打扫,需要打扫的时候调用此方法就好)。
下面我们再从程序的角度来分析一下动态代理模式:
设计动态代理类(DynamicProxy)时,不需要显式实现与目标对象类(RealSubject)相同的接口,而是将这种实现推迟到程序运行时由 JVM来实现,如果不能理解先来看一下实现再去分析吧。
2、代码实现
第一步:定义抽象接口(也就是房子主人让中介做的事:出租房子)
//定义了房屋主人想让中介做的事:出租房子
public interface HouseSubject {
public void rentHouse();
}
第二步:定义具体对象(房屋主人A和B,
//房屋主人A出租房子
public class RealHouseSubjectA implements HouseSubject{
@Override
public void rentHouse() {
System.out.println("我是房子主人A,我要出租房子");
}
}
//房屋主人B出租房子
public class RealHouseSubjectB implements HouseSubject{
@Override
public void rentHouse() {
System.out.println("我是房子主人B,我要出租房子");
}
}
第三步:定义动态代理(一个中介就好)
//一个动态代理照顾好几个
public class DynamicProxy implements InvocationHandler {
// 代理对象
private Object ProxyObject;
public Object newProxyInstance(Object ProxyObject){
this.ProxyObject =ProxyObject;
//生成动态代理类实例
return Proxy.newProxyInstance(
//指定产生代理对象的类加载器
ProxyObject.getClass().getClassLoader(),
//指定目标对象的实现接口
ProxyObject.getClass().getInterfaces(),
//指定InvocationHandler对象
this);
}
// 动态代理每次为房屋主人出租房子时都会调用这个方法
@Override
public Object invoke(Object proxy, Method method, Object[] args)throws Throwable {
System.out.println("===我是动态代理,我要出租房子===");
Object result = method.invoke(ProxyObject, args);
System.out.println("=====我是动态代理,我已经把房子出租出去了=====");
return result;
}
}
第四步:User(演示一个动态代理同时服务好几个)
public class User {
public static void main(String[] args) {
// 1. 创建一个动态代理
DynamicProxy DynamicProxy = new DynamicProxy();
// 2. 创建2个目标对象(房屋主人)
RealHouseSubjectA realHouseSubjectA = new RealHouseSubjectA();
RealHouseSubjectB realHouseSubjectB = new RealHouseSubjectB();
// 3.动态代理拿到相应操作权限:也就是一个中介有了出租多套房子的权限
HouseSubject houseAproxy =
(HouseSubject) DynamicProxy.newProxyInstance(realHouseSubjectA);
HouseSubject houseBproxy =
(HouseSubject) DynamicProxy.newProxyInstance(realHouseSubjectB);
// 4. 通过调用动态代理对象方法从而调用目标对象方法
houseAproxy.rentHouse();
houseBproxy.rentHouse();
}
}
最后再来看一下动态代理的结果:
6-动态代理结果.png
从结果也可以看出,我们的动态代理需要做什么的时候把相应的方法调用就好了,而不是提前完成一些事情。也就是在真正需要执行方法的时候,再去执行,而不是提前先执行。
四、代理模式总结
1、静态代理和动态代理的对比分析
下面使用一张图对他们俩进行一个对比分析:
2、代理模式的缺点
同样的对于整体的代理模式也有很多的缺点:
(1) 由于在客户和客户(商品)之间增加了代理对象,因此可能会造成请求的处理速度变慢
(2) 实现代理模式需要额外的工作,而且有些代理模式的实现过程较为复杂,例如远程代理。
3、代理模式的使用场景
代理模式的类型较多,不同类型的代理模式有不同的优缺点,它们应用于不同的场合:
(1) 当需要访问远程主机对象时使用远程代理。
(2) 当需要用一个消耗资源较少的对象来代表一个消耗资源较多的对象时可以使用虚拟代理,
(3) 频繁操作一个对象,为其设置缓冲区时可以使用缓冲代理。
(4) 为不同的对象提供访问权限时可以使用保护代理。
(5) 当需要为一个对象的访问(引用)提供一些额外的操作时可以使用智能引用代理。
OK,代理模式就先到这里,如果问题还请批评指正
欢迎关注微信公众号:java的架构师技术栈。回复指定关键字可获取编程技术各种视频资源等,包含java基础、进阶、框架、架构师系列。python、Android、微信小程序、神经网络、机器学习等等各种资源
微信公众号.png