文章目录
-
- 什么是装饰器模式
- 设计与实现
-
- 基本接口与文件内容
- 读取一个单词
- 读取一行内容
- 读取所有内容
- 装饰器模式与代理模式的区别
什么是装饰器模式
房子装饰的再好看,它也还是个房子,只是住着比不装修的房子更舒服。
女人打扮的花枝招展,她也还是个女人,但她比素颜女人对男人更加有吸引力。
装饰器模式的概念就是这样,通过对具体对象进行不断的装饰,使其具备更强大的能力,但究其本身,依旧还是那个对象类的概念。
父子类继承关系,可以从类的层面上扩展对象功能,但是继承本身又让类之间有了耦合联系。装饰器模式可以看作是继承的替代方案之一,它是基于对象层面去扩展新功能。
在
Java
里面,
I/O
标准库的设计就很好的展示了装饰器模式的魅力,如果有兴趣可以去看源码,仔细研究体会。
设计与实现
我们不具体分析
Java I/O
展示出来的装饰器模式,但是可以基于它的基本方法,尝试着用装饰器模式实现一个场景。
假设现在有这么一个场景,要读取一个文件的内容,提供三个读取方法
read()
,依次满足:
- 一次可以读取一个单词
- 一次可以读取一行内容
- 一次可以读取整个文件内容
怎么设计代码实现这个场景呢?第一条最好实现,第二条可以建立在第一条之上,第三条可以建立在第二条之上。也就是说,实现的思路是“依次装饰”。
基本接口与文件内容
根据需求场景,可以定义一个简单的接口规范:
public interface StringInputStream {
// 读取文件内容(一个单词、一行内容、所有内容)
String read() throws IOException;
}
文件内容,这是
Mac
系统下编辑的文件,行结尾的符号是
换行键
,对应的
ASCII
码是
10
,空格符对应的
ASCII
码是
32
。
hello word
I tell you
You are beautiful
所以,请注意,以下程序适用于文件换行符是
换行键
的文件,其它文件格式不适用,需要修改!!!
读取一个单词
我们以
Java
自有的
FileInputStream
为基础,就可以很容易的实现这个需求:
// 一次读取一个单词
public class WordInputStream implements StringInputStream {
private FileInputStream fileInputStream;
public WordInputStream(FileInputStream inputStream) {
this.fileInputStream = inputStream;
}
protected StringBuilder read0() throws IOException {
StringBuilder sb = new StringBuilder();
int read;
while ((read = fileInputStream.read()) != -1) {
sb.append((char) read);
if (read == 32 || read == 10) break;
}
return sb.length() == 0 ? null : sb;
}
@Override
public String read() throws IOException {
StringBuilder sb = read0();
if (sb == null) return null;
int charAt = sb.charAt(sb.length() - 1);
if (charAt == 32 || charAt == 10) return sb.substring(0, sb.length() - 1);
return sb.toString();
}
}
// 使用方法
public static void main(String[] args) throws IOException {
FileInputStream fileInputStream = new FileInputStream("word.txt");
WordInputStream wordInputStream = new WordInputStream(fileInputStream);
String word = wordInputStream.read();
}
读取一行内容
包装一下
WordInputStream
类,就可以很容易的读取一行数据:
// 一次读取一行内容
public class LineInputStream implements StringInputStream {
private WordInputStream wordInputStream;
public LineInputStream(WordInputStream inputStream) {
this.wordInputStream = inputStream;
}
protected StringBuilder read0() throws IOException {
StringBuilder sb = new StringBuilder(), temp;
while ((temp = wordInputStream.read0()) != null) {
sb.append(temp);
if (temp.charAt(temp.length() - 1) == 10) break;
}
return sb.length() == 0 ? null : sb;
}
@Override
public String read() throws IOException {
StringBuilder sb = read0();
if (sb == null) return null;
else return sb.toString();
}
}
// 使用方法
public static void main(String[] args) throws IOException {
FileInputStream fileInputStream = new FileInputStream("word.txt");
WordInputStream wordInputStream = new WordInputStream(fileInputStream);
LineInputStream lineInputStream = new LineInputStream(wordInputStream);
String word = lineInputStream.read();
}
读取所有内容
包装一下
LineInputStream
类,就可以很容易的读取一行数据:
// 一次读取所有内容
public class WholeInputStream implements StringInputStream {
private LineInputStream lineInputStream;
public WholeInputStream(LineInputStream lineInputStream) {
this.lineInputStream = lineInputStream;
}
protected StringBuilder read0() throws IOException {
StringBuilder sb = new StringBuilder(), temp;
while ((temp = lineInputStream.read0()) != null) {
sb.append(temp);
}
return sb.length() == 0 ? null : sb;
}
@Override
public String read() throws IOException {
StringBuilder sb = read0();
if (sb == null) return null;
else return sb.toString();
}
}
// 使用方法
public static void main(String[] args) throws IOException {
FileInputStream fileInputStream = new FileInputStream("word.txt");
WordInputStream wordInputStream = new WordInputStream(fileInputStream);
LineInputStream lineInputStream = new LineInputStream(wordInputStream);
WholeInputStream wholeInputStream = new WholeInputStream(lineInputStream);
String word = wholeInputStream.read();
}
以上程序写的较为简单,但是基本也能表达出装饰器模式的用途,可以认为:
-
装饰了WordInputStream
FileInputStream
-
装饰了LineInputStream
WordInputStream
-
装饰了WholeInputStream
LineInputStream
装饰器模式与代理模式的区别
装饰器模式和静态代理模式很像,代码结构也几乎是一模一样的,它们的主要区别不在于代码结构,而在于应用的语境和语义。
设计模式的命名规则都是来源于生活场景,懂得在不同场景下应用不同的设计模式思想,比研究设计模式的代码具体要怎么写更有意义。
抛开具体的编程语言,设计模式的写法应该是千变万化的。
但是针对一种具体的编程语言,设计模式总会有一些流行的常规写法。