设计模式:从Collections.synchronizedCollection()出发了解【装饰器模式】

30 阅读3分钟

 

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
        @Override
        public void forEach(Consumer<? super E> consumer) {
            synchronized (mutex) {c.forEach(consumer);}
        }
        @Override
        public boolean removeIf(Predicate<? super E> filter) {
            synchronized (mutex) {return c.removeIf(filter);}
        }
        @Override
        public Spliterator<E> spliterator() {
            return c.spliterator(); // Must be manually synched by user!
        }
        @Override
        public Stream<E> stream() {
            return c.stream(); // Must be manually synched by user!
        }
        @Override
        public Stream<E> parallelStream() {
            return c.parallelStream(); // Must be manually synched by user!
        }

可以看到,我们无需自己实现这些功能,只用调用传入的原始的c的API,它可以帮我们实现集合的基本功能

这样,我们装饰的新的集合就有原本集合的基本功能了。

再从上面代码来看,其中增删改查的代码在c的方法上加入了synchronized修饰,这样就实现了加锁的功能,也就是装饰器”装饰“的作用。

总结

我们通过实现接口,实现了被装饰的类的基本功能。避免了直接继承Collection,这就是组合优于继承。(如果使用继承实现多种功能组合,会产生大量子类(类爆炸问题) ,而装饰器模式通过动态组合替代继承,减少了类的数量)接下来我们通过修改我们想要改良的方法,实现了装饰器装饰的作用。

我们并没有改变原始对象的接口和结构,符合 "开放扩展、关闭修改" 的设计原则,降低了系统维护风险。

并且我们可以嵌套装饰器,灵活地为对象添加多种功能。