有些朋友们看过《Proxy 那点事儿》与《AOP 那点事儿》以后,提出了一个颇有表明性的问题: java
代理模式与装饰器模式有何区别? 程序员
我想有必要对此问题谈一下个人我的理解,如有误导的之处,还请你们指正! 设计模式
代理模式(Proxy 模式)可理解为:我想作,但不能作,我须要有一个能干的人来帮我作。 缓存
装饰器模式(Decorator 模式)可理解为:我想作,但不能作,我须要有各种特长的人来帮我作,但我有时只须要一我的,有时又须要不少人。 架构
它们的区别就是,Proxy 模式须要的是一个能人,而 Decorator 模式须要的是一个团队。 ide
有些状况下,拥有了一个团队,会更加利于工做分工,而不至于将全部的事情,都让这个能人来干,他终将有一天会 hold 不住的。但有些状况下,人多了反而很差,只须要一个能人就好了。 this
若是这个比喻不太恰当的话,我就要拿出个人杀手锏了,用代码来讲话。 .net
咱们先来回忆一下这两段经典的代码,一个接口,一个它的实现类。 设计
public interface Greeting { void sayHello(String name); }
public class GreetingImpl implements Greeting { @Override public void sayHello(String name) { System.out.println("Hello! " + name); } }
可使用 Proxy 类来代理 GreetingImpl 类作点事情: 代理
public class GreetingProxy implements Greeting { private GreetingImpl greetingImpl; public GreetingProxy(GreetingImpl greetingImpl) { this.greetingImpl = greetingImpl; } @Override public void sayHello(String name) { before(); greetingImpl.sayHello(name); } private void before() { System.out.println("Before"); } }
只需保证 GreetingProxy 与 GreetingImpl 实现同一个接口 Greeting,并经过构造方法将 GreetingImpl 温柔地射入 GreetingProxy 的身体之中,那么,GreetingProxy 就能够彻底拥有 GreetingImpl 了。能够在帮它作正事儿以前,先干点别的事情,好比这里的 before() 方法。想干点什么就干点什么,只要您喜欢,它就喜欢。(此处省略一千字)
以上就是 Proxy 模式,能够认为 GreetingProxy 包装了 GreetingImpl,那么,咱们就应该怎样来使用呢?
public class ClientProxy { public static void main(String[] args) { Greeting greeting = new GreetingProxy(new GreetingImpl()); greeting.sayHello("Jack"); } }
很爽吧?下面用一张类图来表达我此时此刻的感受:
可见,GreetingProxy 是经过“组合”的方式对 GreetingImpl 进行包装,并对其进行功能扩展。这样,无需修改 GreetingImpl 的任何一行代码,就能够完成它想要作的事情。
说的高深一点,这就是“开闭原则”(可不是一开一闭的意思哦),它是设计模式中一条很是重要的原则,意思就是“对扩展开放,对修改封闭”。没错,咱们确实是提供了 GreetingProxy 类来扩展 GreetingImpl 的功能,而并不是去修改 GreetingImpl 原有的代码。这就是超牛逼的“开闭原则”了,每一个开发人员都须要铭记在心!还须要知道的就是扩展并不是只有“继承”这一种方式,这里用到的“组合”也是一种扩展技巧。
其实,以上使用 Proxy 模式实现了 AOP 理论中的 Before Advice(前置加强)功能。若是用户如今来了一个需求,须要在 sayHello 完事以后再记录一点操做日志。那么,咱们此时最简单的方法就是给 GreetingProxy 增长一个 after() 方法,代码以下:
public class GreetingProxy implements Greeting { private GreetingImpl greetingImpl; public GreetingProxy(GreetingImpl greetingImpl) { this.greetingImpl = greetingImpl; } @Override public void sayHello(String name) { before(); greetingImpl.sayHello(name); after(); } private void before() { System.out.println("Before"); } private void after() { System.out.println("After"); } }
这样作确实能够实现需求,但您要知道,需求是永无止境的,这个 Proxy 类未来可能会很是庞大,要干的事情会愈来愈多。一会儿是日志记录,一会儿是事务控制,还有权限控制,还有数据缓存。把全部的功能都放在这个 Proxy 类中是不明智的,同时这也违反了“开闭原则”。
做为一个牛逼的架构师,有必要来点炫的东西,让那帮程序员小弟们对您投来崇拜的目光。
用 Decorator 模式吧!
先来一张牛图:
搞了一个抽象类 GreetingDecorator 出来,确实挺抽象的,它就是传说中的“装饰器”了,也实现了 Greeting 接口(与 Proxy 模式相同),但却有两点不一样:
咱们再也不须要一个能人,而须要一个团队!
若是要加入日志记录功能,能够搞一个日志记录的装饰器;若是要加入事务控制功能,也能够再搞一个事务控制的装饰器;...
想怎么装饰就怎么装饰,这就像您买了一套新房,如今都是毛坯的,您能够刷漆,也能够贴纸,还能够画画,固然能够又刷漆、又贴纸、又画画。
屁话少说,上代码吧!
先来看看这个装饰器:
public abstract class GreetingDecorator implements Greeting { private Greeting greeting; public GreetingDecorator(Greeting greeting) { this.greeting = greeting; } @Override public void sayHello(String name) { greeting.sayHello(name); } }
以上是一个很干净的装饰器,没有任何的加强逻辑,只是简单的经过构造方法射入了 Greeting 对象,而后调用它本身的 sayHello() 方法,感受啥也没干同样。
固然,GreetingDecorator 只是一个抽象的装饰器,要想真正使用它,您得去继承它,实现具体的装饰器才行。
第一个具体装饰器 GreetingBefore:
public class GreetingBefore extends GreetingDecorator { public GreetingBefore(Greeting greeting) { super(greeting); } @Override public void sayHello(String name) { before(); super.sayHello(name); } private void before() { System.out.println("Before"); } }
第二个具体装饰器 GreetingAfter:
public class GreetingAfter extends GreetingDecorator { public GreetingAfter(Greeting greeting) { super(greeting); } @Override public void sayHello(String name) { super.sayHello(name); after(); } private void after() { System.out.println("After"); } }
须要注意的是,在具体装饰器的构造方法中调用了父类的构造方法,也就是把 Greeting 实例射进去了。在具体装饰器中,完成本身应该完成的事情。真正作到了各施其责,而不是一人包揽。
咱们能够这样来用装饰器:
public class ClientDecorator { public static void main(String[] args) { Greeting greeting = new GreetingAfter(new GreetingBefore(new GreetingImpl())); greeting.sayHello("Jack"); } }
先 new GreetingImpl,再 new GreetingBefore,最后 new GreetingAfter。一层裹一层,就像洋葱同样!但不一样的是,裹的顺序是能够交换,好比,先 new GreetingAfter,再 new GreetingBefore。
这种建立对象的方式是否是很是眼熟呢?没错!在 JDK 的 IO 包中也有相似的现象。
好比:想读取一个二进制文件,能够这样获取一个输入流:
InputStream input = new DataInputStream(new BufferedInputStream(new FileInputStream("C:/test.exe")));
其实看看 IO 的类图,就一目了然了,它就用到了 Decorator 模式:
IO 包是一个很强大的包,为了使表达更加简化,以上仅提供了 IO 中的部分类。
到此,想必您已经了解到 Proxy 模式与 Decorator 模式的本质区别了吧?
这里两个模式都是对类的包装,在不改变类自身的状况下,为其添加特定的功能。若这些功能比较单一,可考虑使用 Proxy 模式,但对于功能较多且需动态扩展的状况下,您不妨尝试一下 Decorator 模式吧!
若是本文对您有帮助,那就顶起来吧!