最近有时间, 学习了一下设计模式, 发现了一个问题, 代理模式 (静态代理) 在写法和结构上, 基本和装饰器是一样的.
由此引发了对这两者的真正区别的思考, 网上搜索了许许多多的答案(虽然都有一定的道理, 个人觉得都没有说到真正的重点) :
1 . 有的人说是结构上不同, 代理和真实对象之间的的关系通常在编译时就已经确定了, 而装饰器能够在运行时递归地被构造(我个人完全反对这种说法);
2 . 装饰器模式为了增强功能; 而代理模式是为了加以控制, 代理类对被代理的对象有控制权, 决定其执行或者不执行.(大名鼎鼎的菜鸟教程这样解释);
3 . 甚至还有人说装饰器模式用于添加新方法; 而代理模式则用于增强原方法(那为什么叫代理?).
代理模式 (静态) 与装饰者虽然在结构上基本上一模一样, 但两者却有真正区别, 我认为是 : 目的不一样, 关注的重心不一样.
代理模式目的 : 让原有对象被代理, 我们的目的是让使用者尽可能的感受不到原有对象, 原有对象的行为或额外的动作交由代理对象完成.(完成代理模式的真正意义)
装饰器模式目的 : 让原有对象被增强, 我们的目的通常是得到由原有对象被增强后的装饰器对象行为.(完成装饰器模式的真正意义)
代理模式关注重心 : 主要功能不变, 代理对象只是帮忙代理或稍加扩展原有对象的行为, 功能上主要关心原有对象所具有的行为.(最终主要功能仍然由原有对象决定)
装饰器模式关注重心 : 主要功能增强, 使用装饰器目的就是为了增强, 功能上更关心装饰增加后的行为.(最终主要功能由装饰对象决定)
静态代理
静态代理的角色分为 : 抽象行为角色, 委托人, 代理人. 基本写法如下 :
抽象行为角色 : 是委托人和代理人的共同接口. 这里我们叫它抽象主题(Subject) :
- package name.ealen.proxy.designPattern.staticProxy;
- /**
- * Created by EalenXie on 2018/11/2 10:16.
- */
- public interface Subject {
- public void operation();
- }
委托人 : 也就是我上面一直说的原有对象, 真正被代理的对象, 也叫做代理元. 这里我们叫它真实主题 (RealSubject):
- package name.ealen.proxy.designPattern.staticProxy;
- /**
- * Created by EalenXie on 2018/11/2 10:17.
- */
- public class RealSubject implements Subject {
- @Override
- public void operation() {
- System.out.println("真实对象 : 重要操作");
- }
- }
代理人 : 代理角色, 由它去代理原有对象. 它包含被代理对象的引用, 这里叫它代理主题(ProxySubject):
- package name.ealen.proxy.designPattern.staticProxy;
- /**
- * Created by EalenXie on 2018/11/2 10:18.
- */
- public class ProxySubject implements Subject {
- private Subject subject;
- public ProxySubject(Subject subject) {
- this.subject = subject;
- }
- /**
- * 目的 : 代理真实对象完成方法调用, 代理可以进行相对不重要的行为扩展
- */
- @Override
- public void operation() {
- before();
- subject.operation();
- after();
- }
- private void after() {
- System.out.println("代理人 : 真实对象的操作完成了");
- }
- private void before() {
- System.out.println("代理人 : 开始完成真实对象的操作");
- }
- }
测试代码 :
- /**
- * 静态代理
- */
- @Test
- public void staticProxy() {
- Subject realSubject = new RealSubject(); // 一个真实对象
- Subject proxy = new ProxySubject(realSubject); // 一个代理人, 指定要代理的真实对象, 类型只能是 Subject 及其子类
- proxy.operation(); // 整个操作由代理人帮真实对象完成, 代理人还做了操作说明
- }
结果如下 :
动态代理
动态代理主要依赖 Java 反射机制实现, 基本写法如下 :
抽象行为角色 : 是委托人和代理人的共同接口. 这里我们叫它抽象主题(DynamicSubject) :
- package name.ealen.proxy.designPattern.dynamicProxy;
- /**
- * Created by EalenXie on 2018/11/2 15:35.
- */
- public interface DynamicSubject {
- public void operation() ;
- }
一个被代理对象 , 这里叫他真实对象(DynamicRealSubject) :
- package name.ealen.proxy.designPattern.dynamicProxy;
- /**
- * Created by EalenXie on 2018/11/2 12:56.
- */
- public class DynamicRealSubject implements DynamicSubject {
- @Override
- public void operation() {
- System.out.println("真实对象 : 重要操作");
- }
- }
代理的调用处理类(ProxyHandler) , 主要实现反射的接口 InvocationHandler , 这里可以看出该处理类设计上和被代理对象并没有任何的直接联系 :
- package name.ealen.proxy.designPattern.dynamicProxy;
- import java.lang.reflect.InvocationHandler;
- import java.lang.reflect.InvocationTargetException;
- import java.lang.reflect.Method;
- /**
- * Created by EalenXie on 2018/11/2 12:57.
- * 代理的调用处理类
- */
- public class ProxyHandler implements InvocationHandler {
- private Object realSubject; // 指定被代理的真实对象
- public ProxyHandler(Object realSubject) {
- this.realSubject = realSubject;
- }
- @Override
- public Object invoke(Object proxy, Method method, Object[] args) throws InvocationTargetException, IllegalAccessException {
- before(); // 自定义逻辑
- Object result = method.invoke(realSubject, args); // 调用真实对象的操作
- after(); // 自定义逻辑
- return result;
- }
- private void after() {
- System.out.println("代理人 : 真实对象的操作完成了");
- }
- private void before() {
- System.out.println("代理人 : 开始完成真实对象的操作");
- }
- }
那么如何得到我们的代理对象呢? 答案是基于反射类 Proxy, 既然是动态代理, 那么代理的调用处理类, 是可以代理任何类型的对象. 请看如下测试类 :
- /**
- * 动态代理, 基于反射类实现
- */
- @Test
- public void dynamicProxy() {
- // 一个真实对象
- DynamicSubject subject = new DynamicRealSubject();
- // 动态代理处理逻辑 , 基于反射类 InvocationHandler 实现, 指定要代理的真实对象, 可以是任何类型
- InvocationHandler proxyHandler = new ProxyHandler(subject);
- // 一个代理人, 实例化基于反射类 Proxy 实现.
- DynamicSubject proxy = (DynamicSubject) Proxy.newProxyInstance(DynamicSubject.class.getClassLoader(), subject.getClass().getInterfaces(), proxyHandler);
- // 整个操作由代理人帮真实对象完成, 代理人还做了操作说明
- proxy.operation();
- }
只需要在 ProxyHandler 中动态的传入任何我们需要代理的对象, 然后 Proxy 调用 newProxyInstance, 强转即可得到我们的代理对象, 结果如下 :
来源: https://www.cnblogs.com/ealenxie/p/9896269.html