区别介绍
装饰模式和代理模式看起来很像。对装饰模式来说,装饰者(decorator)和被装饰者(decoratee)都实现同一个接口。对代理模式来说,代理类(proxy class)和真实处理的类(real class)都实现同一个接口。此外,不论我们使用哪一个模式,都可以很容易地在真实对象的方法前面或者后面加上自定义的方法。
实际上,二者的目的不一样,关注的重心不一样。装饰模式关注于在一个对象上动态的添加方法,代理模式关注于控制对对象的访问。装饰模式指的是在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰者来包裹真实的对象;代理模式是为其它对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
1 | 代理模式目的:让原有对象被代理,让使用者尽可能的感受不到原有对象,原有对象的行为或额外的动作交由代理对象完成。(完成代理模式的真正意义) |
网上说法(存疑)
用代理模式,代理类(proxy class)可以对它的客户隐藏一个对象的具体信息。因此,当使用代理模式的时候,我们常常在一个代理类中创建(new)一个对象的实例。而当我们使用装饰模式的时候,我们通常的做法是将原始对象作为一个参数传给装饰者的构造器。我们可以用另外一句话来总结这些差别:使用代理模式,代理和真实对象之间的的关系通常在编译时就已经确定了,而装饰者能够在运行时递归地被构造。
代理模式:
1 | //代理模式 |
装饰模式:
1 | //装饰模式 |
结构图比较
下面是代理模式的结构图:
代理对象Proxy和被代理对象RealSubject都继承了Subject接口。客户端调用Proxy的方法,而Proxy则把具体操作委托给RealSubject执行。下面是代码实现:
1 | interface Subject { |
输出如下:
1 | RealSubject#doAction |
在Client中,首先创建了一个realSubject 对象,然后创建一个代理对象proxy并且把realSubject对象通过构造器传入进去。最后调用代理对象的doAction,实际执行的是realSubject的对应方法。这里通过构造函数的参数将被代理对象传入到代理中,也可以通过其它方式,如提供一个setSubject方法。
上面的代理模式,代理对象和被代理对象需要实现相同的接口,所以如果要代理其它接口的对象需要写一个新的代理类。Java提供了动态代理的功能,可以简化我们的代码。
动态代理可以在运行期生成所需要的代理对象,看下面的代码:
1 | class DynamicProxy implements InvocationHandler { |
类DynamicProxy实现了InvocationHandler接口,这个接口中有一个invoke方法,被代理的对象的任何方法都是在invoke中调用。下面是Client的代码:
1 | public class TestDelegate { |
输出如下:
1 | RealSubject#doAction |
首先依然是先创建一个需要被代理的对象realSubject,然后把它传入到DynamicProxy的构造函数中。这个dynamicProxy还不是我们需要的代理,毕竟它没有实现Subject接口。下面通过Proxy.newProxyInstance创建了一个Subject对象,也就是最终的代理对象。
通过动态代理,创建一个实现了InvocationHandler接口的DynamicProxy类,通过这个类可以在运行期为各种对象创建对应的代理,比静态代理方便了很多。
下面是装饰模式的结构图:
从上图可以看出,装饰者Decorator与需要被装饰的对象ContcreteComponent实现了相同的接口。具体怎么装饰则由Decorator的子类ConcreteDecorator决定。
Java中使用装饰者模式的一个典型的例子是I/O对象的创建,比如创建一个BufferedInputStream时:
1 | InputStream in = ... |
BufferedInputStream继承于FilterInputStream,这个FilterInputStream相当于装饰者模式中的Decorator,它继承了InputStream接口。BufferedInputStream则是一个具体的装饰类,其它还有DataInputStream以及ByteArrayInputStream等。而传给BufferedInputstream的对象in则是需要被装饰者。装饰者对被装饰者进行了功能的扩展,但是又不需要修改被装饰者的相应代码,符合“开闭原则”,即对于修改是封闭的,对于扩展则是开放的。
如果是为了给某个类提供更多的功能,继承是一种方案。但是,如果我们的功能有很多种组合,那么为每种组合编写一个继承的类可能需要创建太多的子类。而装饰者模式则可以解决这个问题,只需要为每个功能编写一个装饰类,在运行时组合不同的对象即可实现所需的功能组合。
参考资料:
然则 代理模式和装饰者模式
风雪漫中州 设计模式-代理模式(和装饰模式的真正区别)
shuzhou12 装饰模式与代理模式的区别