共计 8312 个字符,预计需要花费 21 分钟才能阅读完成。
前言
本篇文章分为四个部分:第一部分会举一个例子引出装饰者模式,让读者对装饰者模式有个感官上的认识;第二部分会给出装饰者模式的定义(当然我们主要不是来背定义,就当做积累专业名词来记吧,我个人是很不喜欢下定义的);第三部分,我会拿 jdk 中两个使用装饰者模式的例子进行分析,让读者在学习模式的同时熟悉一下 jdk 源码。第四部分,我会结合装饰者模式理清楚 java I/ O 各种流之间的关系(说实话,工作中用得最多,但是记了又忘,因为 I / O 类实在太多)后面学习到其他框架的时候再补充。
第一部分
假如有这样的需求:要求实现只能够读的 List, 要是你来完成这个任务你会如何做?
看下面代码实现:
package decorate;
import java.util.*;
public class ReadOnlyList<E> implements List<E>{
private List<E> target;
public ReadOnlyList(List<E> target) {super();
this.target = target;
}
@Override
public int size() {return target.size();
}
@Override
public boolean isEmpty() {return target.isEmpty();
}
@Override
public boolean contains(Object o) {return target.contains(o);
}
@Override
public Iterator<E> iterator() {return target.iterator();
}
@Override
public Object[] toArray() {return target.toArray();
}
@Override
public <T> T[] toArray(T[] a) {return target.toArray(a);
}
@Override
public boolean add(E e) {throw new RuntimeException("only read");
}
@Override
public boolean remove(Object o) {throw new RuntimeException("only read");
}
@Override
public boolean containsAll(Collection<?> c) {return target.containsAll(c);
}
@Override
public boolean addAll(Collection<? extends E> c) {throw new RuntimeException("only read");
}
@Override
public boolean removeAll(Collection<?> c) {throw new RuntimeException("only read");
}
@Override
public boolean retainAll(Collection<?> c) {throw new RuntimeException("only read");
}
@Override
public void clear() {throw new RuntimeException("only read");
}
@Override
public boolean addAll(int index, Collection<? extends E> c) {throw new RuntimeException("only read");
}
@Override
public E get(int index) {return target.get(index);
}
@Override
public E set(int index, E element) {throw new RuntimeException("only read");
}
@Override
public void add(int index, E element) {throw new RuntimeException("only read");
}
@Override
public E remove(int index) {throw new RuntimeException("only read");
}
@Override
public int indexOf(Object o) {return target.indexOf(o);
}
@Override
public int lastIndexOf(Object o) {return target.lastIndexOf(o);
}
@Override
public ListIterator<E> listIterator() {return target.listIterator();
}
@Override
public ListIterator<E> listIterator(int index) {return target.listIterator(index);
}
@Override
public List<E> subList(int fromIndex, int toIndex) {return target.subList(fromIndex, toIndex);
}
}
这里的 set,add,remove 和 addAll 等操作都被限制了,只能够读。
测试类
package decorate;
import java.util.ArrayList;
import java.util.List;
public class ReadOnlyListMain {public static void main(String[] args) {List<String> list = new ArrayList<>();
list.add("设计模式");
list.add("装饰者模式");
list.add("工厂模式");
ReadOnlyList onlyList = new ReadOnlyList<>(list);
// onlyList.add("只能读啦"); // 会报错只读异常
System.out.println(onlyList.size()); // 可以正确运行
}
}
第二部分 定义
Component(抽象组件): 是具体组件和抽象装饰类的共同父类,声明了在具体组件中实现的方法。比如第一部分的 List
ConcreteComponent(具体组件): 抽象组件的子类,实现抽象组件的方法,装饰器可以给他加新的功能。比如 ReadOnlyList
Decorator(抽象装饰者): 抽象组件的子类,用来装饰具体组件或者装饰其他装饰组件
ConcreteDecorator(具体装饰者): 抽象装饰类的子类
具体的可以看下面的结构图:
第三部分 jdk 例子分析
第一部分我们引出装饰者模式,ReadOnlyList 只读 List 将 List 给包装起来,提供了只读的功能,jdk Collection 中也有个类似的实现:
public static <T> List<T> unmodifiableList(List<? extends T> list)
让我来看看它的源码:
public static <T> List<T> unmodifiableList(List<? extends T> list) {
return (list instanceof RandomAccess ?
new UnmodifiableRandomAccessList<>(list) :
new UnmodifiableList<>(list));
}
我们进入 UnmodifiableList
/**
* @serial include
*/
static class UnmodifiableList<E> extends UnmodifiableCollection<E>
implements List<E> {
private static final long serialVersionUID = -283967356065247728L;
final List<? extends E> list;
UnmodifiableList(List<? extends E> list) {super(list);
this.list = list;
}
public boolean equals(Object o) {return o == this || list.equals(o);}
public int hashCode() {return list.hashCode();}
public E get(int index) {return list.get(index);}
public E set(int index, E element) {throw new UnsupportedOperationException();
}
public void add(int index, E element) {throw new UnsupportedOperationException();
}
public E remove(int index) {throw new UnsupportedOperationException();
}
public int indexOf(Object o) {return list.indexOf(o);}
public int lastIndexOf(Object o) {return list.lastIndexOf(o);}
public boolean addAll(int index, Collection<? extends E> c) {throw new UnsupportedOperationException();
}
@Override
public void replaceAll(UnaryOperator<E> operator) {throw new UnsupportedOperationException();
}
@Override
public void sort(Comparator<? super E> c) {throw new UnsupportedOperationException();
}
public ListIterator<E> listIterator() {return listIterator(0);}
public ListIterator<E> listIterator(final int index) {return new ListIterator<E>() {
private final ListIterator<? extends E> i
= list.listIterator(index);
public boolean hasNext() {return i.hasNext();}
public E next() {return i.next();}
public boolean hasPrevious() {return i.hasPrevious();}
public E previous() {return i.previous();}
public int nextIndex() {return i.nextIndex();}
public int previousIndex() {return i.previousIndex();}
public void remove() {throw new UnsupportedOperationException();
}
public void set(E e) {throw new UnsupportedOperationException();
}
public void add(E e) {throw new UnsupportedOperationException();
}
@Override
public void forEachRemaining(Consumer<? super E> action) {i.forEachRemaining(action);
}
};
}
public List<E> subList(int fromIndex, int toIndex) {return new UnmodifiableList<>(list.subList(fromIndex, toIndex));
}
private Object readResolve() {
return (list instanceof RandomAccess
? new UnmodifiableRandomAccessList<>(list)
: this);
}
}
你们发现了啥,没错,就是它:addAll、replaceAll 和 sort 等都抛出 UnsupportedOperationException 异常,说明只支持读。
还没结束,再举个例子,Collections 中还有将线程不安全的集合转换成线程安全的集合 synchronizedList,也就是使用装饰者模式,本质上就是在方法加上 synchronized 同步锁
让我们看源码:
public static <T> List<T> synchronizedList(List<T> list) {
return (list instanceof RandomAccess ?
new SynchronizedRandomAccessList<>(list) :
new SynchronizedList<>(list));
}
老规矩,进入 SynchronizedList
/**
* @serial include
*/
static class SynchronizedList<E>
extends SynchronizedCollection<E>
implements List<E> {
private static final long serialVersionUID = -7754090372962971524L;
final List<E> list;
SynchronizedList(List<E> list) {super(list);
this.list = list;
}
SynchronizedList(List<E> list, Object mutex) {super(list, mutex);
this.list = list;
}
public boolean equals(Object o) {if (this == o)
return true;
synchronized (mutex) {return list.equals(o);}
}
public int hashCode() {synchronized (mutex) {return list.hashCode();}
}
public E get(int index) {synchronized (mutex) {return list.get(index);}
}
public E set(int index, E element) {synchronized (mutex) {return list.set(index, element);}
}
public void add(int index, E element) {synchronized (mutex) {list.add(index, element);}
}
public E remove(int index) {synchronized (mutex) {return list.remove(index);}
}
public int indexOf(Object o) {synchronized (mutex) {return list.indexOf(o);}
}
public int lastIndexOf(Object o) {synchronized (mutex) {return list.lastIndexOf(o);}
}
public boolean addAll(int index, Collection<? extends E> c) {synchronized (mutex) {return list.addAll(index, c);}
}
public ListIterator<E> listIterator() {return list.listIterator(); // Must be manually synched by user
}
public ListIterator<E> listIterator(int index) {return list.listIterator(index); // Must be manually synched by user
}
public List<E> subList(int fromIndex, int toIndex) {synchronized (mutex) {return new SynchronizedList<>(list.subList(fromIndex, toIndex),
mutex);
}
}
@Override
public void replaceAll(UnaryOperator<E> operator) {synchronized (mutex) {list.replaceAll(operator);}
}
@Override
public void sort(Comparator<? super E> c) {synchronized (mutex) {list.sort(c);}
}
private Object readResolve() {
return (list instanceof RandomAccess
? new SynchronizedRandomAccessList<>(list)
: this);
}
嘿嘿,我们现在已经学会从设计模式的角度看 jdk 源码啦,开心。
第四部分 Java I/ O 流中的装饰者模式
我们来看看 Java I/ O 中的流图
我们以输入流来分析,首先 InputStream 相当于我们的抽象组件,FileInputStream、StringBufferInputStream、ByteArrayInputStream 都是可以被装饰者包装起来的组件,FilterInputStream 相当于抽象装饰者,PushbackInputStream、BufferedInputStream、DataInputStream 和 LineNumberInputStream 都是具体的装
饰者
他们可以这样用: 一层装饰一层
InputStream ip = new DataInputStream(new BufferedInputStream(new FileInputStream(new File("/file_path"))));
如下图所示:
有了这样的思路,我们以后想编写自己的 I /O,给流增加新的特性,我们就可以继承 FilterInputStream,
package decorate;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
/**
* 将输入的流内所有的大写字符转成小写字符
*/
public class LowerCaseInputStream extends FilterInputStream{public LowerCaseInputStream(InputStream in) {super(in);
}
@Override
public int read() throws IOException {int c = super.read();
if (c == -1) {return c;} else {return Character.toLowerCase((char)c);
}
}
@Override
public int read(byte[] b, int off, int len) throws IOException {int result = super.read(b, off, len);
for (int i = off; i < off + result; i++) {b[i] = (byte) Character.toLowerCase((char)b[i]);
}
return result;
}
}
测试类:把文件路径替换成你自己的路径
package decorate;
import java.io.*;
/**
* 测试类
*/
public class MyInputStreamTest {public static void main(String[] args) {
int c;
try {InputStream in = new LowerCaseInputStream(new BufferedInputStream(new FileInputStream("file_path")));
while((c = in.read()) >= 0) {System.out.println((char)c);
}
} catch (IOException e) {e.printStackTrace();
}
}
}