banner
NEWS LETTER

设计模式-代理模式(和装饰模式的真正区别)

Scroll down

开篇

最近有时间,学习了一下设计模式,发现了一个问题,代理模式(静态代理)在写法和结构上,基本和装饰器是一样的。

由此引发了对这两者的真正区别的思考,网上搜索了许许多多的答案(虽然都有一定的道理,个人觉得都没有说到真正的重点) :

  1. 有的人说是结构上不同,代理和真实对象之间的的关系通常在编译时就已经确定了,而装饰器能够在运行时递归地被构造(我个人完全反对这种说法);

  2. 装饰器模式为了增强功能;而代理模式是为了加以控制,代理类对被代理的对象有控制权,决定其执行或者不执行。(大名鼎鼎的菜鸟教程这样解释);

  3. 甚至还有人说装饰器模式用于添加新方法;而代理模式则用于增强原方法(那为什么叫代理?)。

代理模式(静态)与装饰者虽然在结构上基本上一模一样,但两者却有真正区别,我认为是 : 目的不一样,关注的重心不一样。

  代理模式目的 : 让原有对象被代理,我们的目的是让使用者尽可能的感受不到原有对象,原有对象的行为或额外的动作交由代理对象完成。(完成代理模式的真正意义)

  装饰器模式目的 : 让原有对象被增强,我们的目的通常是得到由原有对象被增强后的装饰器对象行为。(完成装饰器模式的真正意义)

  代理模式关注重心 : 主要功能不变,代理对象只是帮忙代理或稍加扩展原有对象的行为,功能上主要关心原有对象所具有的行为。(最终主要功能仍然由原有对象决定)

  装饰器模式关注重心 : 主要功能增强,使用装饰器目的就是为了增强,功能上更关心装饰增加后的行为。(最终主要功能由装饰对象决定)

静态代理

静态代理的角色分为 : 抽象行为角色,委托人,代理人。基本写法如下 :

  抽象行为角色 : 是委托人和代理人的共同接口。这里我们叫它抽象主题(Subject) :

1
2
3
4
5
6
7
8
9
10
package name.ealen.proxy.designPattern.staticProxy;

/**
* Created by EalenXie on 2018/11/2 10:16.
*/
public interface Subject {

public void operation();

}

  委托人 : 也就是我上面一直说的原有对象,真正被代理的对象,也叫做代理元。这里我们叫它真实主题 (RealSubject):

1
2
3
4
5
6
7
8
9
10
11
12
13
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):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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("代理人 : 开始完成真实对象的操作");
}
}

** 测试代码 : **

1
2
3
4
5
6
7
8
9
10
11
12
/**
* 静态代理
*/
@Test
public void staticProxy() {

Subject realSubject = new RealSubject(); //一个真实对象

Subject proxy = new ProxySubject(realSubject); //一个代理人,指定要代理的真实对象,类型只能是Subject及其子类

proxy.operation(); //整个操作由代理人帮真实对象完成,代理人还做了操作说明
}

  结果如下 :

动态代理

动态代理主要依赖Java反射机制实现,基本写法如下 :

  抽象行为角色 : 是委托人和代理人的共同接口。这里我们叫它抽象主题(DynamicSubject) :

1
2
3
4
5
6
7
8
package name.ealen.proxy.designPattern.dynamicProxy;

/**
* Created by EalenXie on 2018/11/2 15:35.
*/
public interface DynamicSubject {
public void operation() ;
}

  一个被代理对象 ,这里叫他真实对象(DynamicRealSubject) :

1
2
3
4
5
6
7
8
9
10
11
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 ,这里可以看出该处理类设计上和被代理对象并没有任何的直接联系 :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
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,既然是动态代理,那么代理的调用处理类,是可以代理任何类型的对象(但动态代理规定该对象必须实现一个或多个接口)。请看如下测试类 :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* 动态代理,基于反射类实现
*/
@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,强转即可得到我们的代理对象,结果如下 :

其他文章
目录导航 置顶
  1. 1. 开篇
  2. 2. 静态代理
  3. 3. 动态代理
请输入关键词进行搜索