按照代理的创建时期,代理类可以分为静态代理和动态代理。
1.动态代理:在程序运行时,运用反射机制动态创建而成。
2.静态代理:由程序员创建或特定工具自动生成源代码,再对其编译,在程序运行前,代理类的.class文件就已经存在了。
两者对比:静态代理只能代理一种类型的被代理类,换个类型的就不行了,这需要动态代理。
接下来我们以智能引用代理为例,分别采用静态代理和动态代理的方式实现。
继承与聚合的方式实现静态代理
静态代理的代理和被代理对象在代理之前都是确定的。他们都实现相同的接口或者继承相同的抽象类。
示例演示:
假设一辆小车有一个行驶的方法,我们通过代理实现这个行驶的方法,同时增加记录行驶时间的方法:
1 | // 首先定义接口Moveable.java: |
通过继承的方式定义Car2来实现静态代理
1 | public class Car2 extends Car { |
通过聚合的方式定义Car3来实现静态代理
1 | public class Car3 implements Moveable { |
测试代码与结果
1 | public class Client { |
测试结果如下:
1 | 汽车开始行驶 |
两种方式的比较
静态代理实现有如下两种方法:
(1)继承法:代理类直接继承被代理类,实现其原有方法,并添加一些额外功能。
(2)聚合方法:代理类实现相同的功能接口(很重要,相同接口,不同代理也可以进行相互代理)并在内声明一个被代理类的对象(类似封装),通过内部对象实现其原有方法,并添加额外功能。
那么继承法和聚合法何者更优呢?结论是聚合比继承更适合实现代理。
如果我们要实现功能的叠加,比如增加方法运行时间处理,增加权限管理,增加日志处理等,或者调整这些功能的实现顺序,如果有一百种实现顺序,我们用继承法就要实现100个代理子类,这显然是不现实的,如果需要修改也是极其繁琐的。而如果使用聚合法,只要一个代理类管理一个功能,在维护和调整顺序上都是最优的。
聚合方式的静态代理,实现功能的叠加
下面我们实现汽车行驶过程中记录日志和行驶时间的功能。
记录行驶时间的代理
1 | public class CarTimeProxy implements Moveable { |
记录行驶日志的代理
1 | public class CarLogProxy implements Moveable { |
测试
先记录日志再记录时间
1 | public class Client { |
测试结果如下:
1 | 日志开始 |
通过将记录时间的实例传递给记录日志的实例,实现了先记录日志再记录时间的操作。如果想实现先记录时间再记录日志,将两个实例对象的传递交换一下就能够实现想要的操作。
测试结果如下:
1 | 汽车开始行驶 |
代码详见 PatternTest
参考资料:
BestWZR 模式的秘密——代理模式
David 模式的秘密——代理模式