设计模式:从Collections.synchronizedCollection()出发了解【装饰器模式】
Collections.synchronizedCollection()的作用?
相信大家在阅读八股文的时候,都会了解一个问题:如何实现一个线程安全的ArrayList?其中Collections.synchronizedList()就是解决方法之一。那么这个方法是如何实现在原有的ArrayList基础上使其线程安全的?这就用到了装饰器模式。
什么是装饰器模式?
装饰器模式(Decorator Pattern)是一种结构型设计模式,它允许在不改变原有对象结构的情况下,动态地给对象添加新的功能。装饰器模式通过创建一个包装对象(装饰器)来包裹原始对象,从而在保持原始对象接口不变的前提下扩展其功能。
要理解装饰器模式,最形象的现实例子就是 “手机个性化定制”—— 手机本身是核心功能载体,而贴膜、戴壳、装磁吸支架等操作,都是在不改变手机原有功能(通话、上网、拍照)的前提下,动态给手机添加新能力(防刮、防摔、方便导航),且这些 “装饰” 还能自由组合、随时更换。
源码分析
接下来,我通过阅读Collections.synchronizedCollection()的源码,来具体说明一下装饰器模式是如何进行的:
首先看到从外部传入了我们想要包装的原始数组,然后进入SynchronizedCollection方法。
这个方法实现了Collection接口,说明它可以实现Collection所有的基本方法。
要想实现这些方法,我们要注意到在这其中声明了一个不可变的集合c,这是通过构造函数将上文传入的集合赋值给c。从一下代码可以看到c的作用。
public int size() {synchronized (mutex) {return c.size();}}public boolean isEmpty() {synchronized (mutex) {return c.isEmpty();}}public boolean contains(Object o) {synchronized (mutex) {return c.contains(o);}}public Object[] toArray() {synchronized (mutex) {return c.toArray();}}public <T> T[] toArray(T[] a) {synchronized (mutex) {return c.toArray(a);}}public <T> T[] toArray(IntFunction<T[]> f) {synchronized (mutex) {return c.toArray(f);}}public Iterator<E> iterator() {return c.iterator(); // Must be manually synched by user!}public boolean add(E e) {synchronized (mutex) {return c.add(e);}}public boolean remove(Object o) {synchronized (mutex) {return c.remove(o);}}public boolean containsAll(Collection<?> coll) {synchronized (mutex) {return c.containsAll(coll);}}public boolean addAll(Collection<? extends E> coll) {synchronized (mutex) {return c.addAll(coll);}}public boolean removeAll(Collection<?> coll) {synchronized (mutex) {return c.removeAll(coll);}}public boolean retainAll(Collection<?> coll) {synchronized (mutex) {return c.retainAll(coll);}}public void clear() {synchronized (mutex) {c.clear();}}public String toString() {synchronized (mutex) {return c.toString();}}// Override default methods in Collection@Overridepublic void forEach(Consumer<? super E> consumer) {synchronized (mutex) {c.forEach(consumer);}}@Overridepublic boolean removeIf(Predicate<? super E> filter) {synchronized (mutex) {return c.removeIf(filter);}}@Overridepublic Spliterator<E> spliterator() {return c.spliterator(); // Must be manually synched by user!}@Overridepublic Stream<E> stream() {return c.stream(); // Must be manually synched by user!}@Overridepublic Stream<E> parallelStream() {return c.parallelStream(); // Must be manually synched by user!}
可以看到,我们无需自己实现这些功能,只用调用传入的原始的c的API,它可以帮我们实现集合的基本功能。
这样,我们装饰的新的集合就有原本集合的基本功能了。
再从上面代码来看,其中增删改查的代码在c的方法上加入了synchronized修饰,这样就实现了枷锁的功能,也就是装饰器”装饰“的作用。
总结
我们通过实现接口,实现了被装饰的类的基本功能。避免了直接继承Collection,这就是组合优于继承。(如果使用继承实现多种功能组合,会产生大量子类(类爆炸问题),而装饰器模式通过动态组合替代继承,减少了类的数量)接下来我们通过修改我们想要改良的方法,实现了装饰器装饰的作用。
我们并没有改变原始对象的接口和结构,符合 "开放扩展、关闭修改" 的设计原则,降低了系统维护风险。
并且我们可以嵌套装饰器,灵活地为对象添加多种功能。