前言
本节从装饰者模式到Java 里的 IO。装饰者模式能够在不修改任何底层类代码的情况下,给你的(或别人的) 对象赋予新的职责。属于结构型设计模式。符合开闭原则,但是会产生更多的类,提高程序复杂性。
代码实现
// 抽象实体类
public abstract class Cake {
protected abstract String getDesc();
protected abstract int cost();
}
// 确定的实体类
public class RelCake extends Cake {
@Override
public String getDesc() {
return "这是一个蛋糕";
}
@Override
public int cost() {
return 150;
}
}
// 抽象装饰者
public abstract class Decorator extends Cake {
private Cake cake;
public Decorator(Cake cake) {
this.cake = cake;
}
// 抽象装饰者可以通过此方法保证装饰者实现此方法 (拓展)
// protected abstract void doSomething();
@Override
public String getDesc() {
return cake.getDesc();
}
@Override
public int cost() {
return cake.cost();
}
}
// 具体装饰者
// 往蛋糕里面加葡萄
public class CakeWithGrapes extends Decorator {
public CakeWithGrapes(Cake cake) {
super(cake);
}
@Override
public String getDesc() {
return super.getDesc() + " 加一份葡萄";
}
@Override
public int cost() {
return super.cost() + 20;
}
}
// 往蛋糕里加草莓
public class CakeWithStra extends Decorator {
public CakeWithStra(Cake cake) {
super(cake);
}
@Override
public String getDesc() {
return super.getDesc() + " 加一份草莓";
}
@Override
public int cost() {
return super.cost() + 30;
}
}
// 测试
public class DecoratorTest {
public static void main(String[] args) {
// 声明抽象实体
Cake cake;
// 赋值具体实体
cake = new RelCake();
// 进行装饰
cake = new CakeWithGrapes(cake);
cake = new CakeWithGrapes(cake);
cake = new CakeWithStra(cake);
System.out.println(cake.getDesc() + "价钱:" + cake.cost());
}
}
UML
角色:抽象的实体类,确定的实体类。抽象的装饰者,确定的装饰者。
Java I/O
InputStream 作为基类,抽象组件
一类是直接提供数据的基础InputStream:抽象装饰者
- FileInputStream
- ByteArrayInputStream
- ServletInputStream
- ...
一类是提供额外附加功能的InputStream:具体装饰者
- BufferedInputStream
- DigestInputStream
- CipherInputStream
- ...
当我们需要给一个“基础”InputStream附加各种功能时,我们先确定这个能提供数据源的InputStream,因为我们需要的数据总得来自某个地方,例如,FileInputStream,数据来源自文件:
InputStream file = new FileInputStream("test.gz");
紧接着,我们希望FileInputStream能提供缓冲的功能来提高读取的效率,因此我们用BufferedInputStream包装这个InputStream,得到的包装类型是BufferedInputStream,但它仍然被视为一个InputStream:
InputStream buffered = new BufferedInputStream(file);
无论我们包装多少次,得到的对象始终是InputStream
注意到在叠加多个FilterInputStream,我们只需要持有最外层的InputStream,并且,当最外层的InputStream关闭时(在try(resource)块的结束处自动关闭),内层的InputStream的close()方法也会被自动调用,并最终调用到最核心的“基础”InputStream,因此不存在资源泄露。
自己实现一个 LowerCaseInputStream
对文件中的字符串将大写字符小写处理
// 拓展FilterInputStream 这是所有InputStream 的抽象装饰者
public class LowerCaseInputStream extends FilterInputStream {
protected LowerCaseInputStream(InputStream in) {
super(in);
}
// 针对字节
@Override
public int read() throws IOException {
int cur = super.read();
return ((cur == -1) ? cur : Character.toLowerCase((char) cur));
}
// 针对字节数组
@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;
}
}
public class FileTest {
public static void main(String[] args) {
int c;
try {
// 对 InputStream 的包装
InputStream inputStream = new FileInputStream("yourPath\\test.txt");
inputStream = new BufferedInputStream(inputStream);
inputStream = new LowerCaseInputStream(inputStream);
// 读取字符进行输出
while ((c = inputStream.read()) >= 0) {
System.out.print((char) c);
}
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
test.txt 内容:What are you Doing now?
控制台输出:what are you doing now?
总结
通过在继承上面加入组合的方式实现装饰。抽象策略将抽象实体用构造器传入,从而对抽象实体进行装饰。 可以在运行时给某个功能添加附加的功能。
References
- https://www.liaoxuefeng.com/
- 《Head First 设计模式》