标签:耦合 test buffer 文件 des 封装 角色 部分 道具
装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。
业务场景:现我们现在模拟这样一个场景,我们点了一杯奶茶,然后给奶茶中加了冰块,加了珍珠,最后我们还想再给加点红豆,这里加红豆就使用了装饰者。
我们先来创建一个奶茶的抽象类,这个就是上面的Component
角色
public interface MilkyTea {
public void recipe();
}
我们再来创建要给奶茶的具体子类,相当于ConcreteComponent
public class MilkyTeaA implements MilkyTea {
@Override
public void recipe() {
System.out.println("老板来一杯奶茶,加冰块");
}
}
接下来创建一个装饰类,相当于Decorator
public class Decorator implements MilkyTea {
private MilkyTea milkyTea;
public void setMilkyTea(MilkyTea milkyTea) {
this.milkyTea = milkyTea;
}
@Override
public void recipe() {
milkyTea.recipe();
}
}
创建装饰类的子类,添加珍珠,相当于ConcreteDecorator
public class MilkyTeaADecorator extends Decorator {
@Override
public void recipe() {
super.recipe();
//对现有类进行功能增强
recipeZZ();
}
// 加珍珠
public void recipeZZ() {
System.out.println("老板再加点珍珠吧");
}
}
创建装饰者的子类,添加红豆,相当于ConcreteDecorator
public class MilkyTeaBDecorator extends Decorator {
@Override
public void recipe() {
super.recipe();
recipeHD();
}
public void recipeHD() {
System.out.println("老板你再给加点红豆吧");
}
}
最后我们测试一下看下结果:
public class Test {
public static void main(String[] args) {
MilkyTeaA milkyTea = new MilkyTeaA();
MilkyTeaADecorator milkyTeaA = new MilkyTeaADecorator();
MilkyTeaBDecorator milkyTeaB = new MilkyTeaBDecorator();
milkyTeaA.setMilkyTea(milkyTea);
milkyTeaB.setMilkyTea(milkyTeaA);
milkyTeaB.recipe();
}
}
在JDK
中,IO部分的很多类用到了装饰器模式。
InputStream
作为抽象构件(Component
),其下面大约有如下几种具体基础构件(ConcreteComponent
),从不同的数据源产生输入:
ByteArrayInputStream
,从字节数组产生输入;FileInputStream
,从文件产生输入;StringBufferInputStream
,从String对象产生输入;PipedInputStream
,从管道产生输入;SequenceInputStream
,可将其他流收集合并到一个流内;FilterInputStream
作为装饰器在JDK
中是一个普通类,其下面有多个具体装饰器比如BufferedInputStream
、DataInputStream
等。我们以BufferedInputStream
为例,使用它就是避免每次读取时都进行实际的写操作,起着缓冲作用。我们可以在这里稍微深入一下,站在源码的角度来管中窥豹。
FilterInputStream内部封装了基础构件:
protected volatile InputStream in;
而BufferedInputStream
在调用其read()读取数据时会委托基础构件来进行更底层的操作,而它自己所起的装饰作用就是缓冲,在源码中可以很清楚的看到这一切:
public synchronized int read() throws IOException {
if (pos >= count) {
fill();
if (pos >= count)
return -1;
}
return getBufIfOpen()[pos++] & 0xff;
}
private void fill() throws IOException {
byte[] buffer = getBufIfOpen();
if (markpos < 0)
pos = 0; /* no mark: throw away the buffer */
else if (pos >= buffer.length) /* no room left in buffer */
if (markpos > 0) { /* can throw away early part of the buffer */
int sz = pos - markpos;
System.arraycopy(buffer, markpos, buffer, 0, sz);
pos = sz;
markpos = 0;
} else if (buffer.length >= marklimit) {
markpos = -1; /* buffer got too big, invalidate mark */
pos = 0; /* drop buffer contents */
} else if (buffer.length >= MAX_BUFFER_SIZE) {
throw new OutOfMemoryError("Required array size too large");
} else { /* grow buffer */
int nsz = (pos <= MAX_BUFFER_SIZE - pos) ?
pos * 2 : MAX_BUFFER_SIZE;
if (nsz > marklimit)
nsz = marklimit;
byte nbuf[] = new byte[nsz];
System.arraycopy(buffer, 0, nbuf, 0, pos);
if (!bufUpdater.compareAndSet(this, buffer, nbuf)) {
throw new IOException("Stream closed");
}
buffer = nbuf;
}
count = pos;
// 看这行就行了,委托基础构件来进行更底层的操作
int n = getInIfOpen().read(buffer, pos, buffer.length - pos);
if (n > 0)
count = n + pos;
}
private InputStream getInIfOpen() throws IOException {
InputStream input = in;
if (input == null)
throw new IOException("Stream closed");
return input;
}
这部分的代码很多,这里我们没有必要考虑这段代码的具体逻辑,只需要看到在BufferedInputStream的read方法中通过getInIfOpen()获取基础构件从而委托其进行更底层的操作(在这里是读取单个字节)就可以说明本文所要说的一切了。
至于I/O类库中的其他设计诸如OutputStream、Writer、Reader,是一致的,这里就不再赘述了。
但是也有其自身的缺点:
多层的装饰是比较复杂的。为什么会复杂?你想想看,就像剥洋葱一样,你剥到最后才发现是最里层的装饰出现了问题,可以想象一下工作量。这点从我使用Java I/O的类库就深有感受,我只需要单一结果的流,结果却往往需要创建多个对象,一层套一层,对于初学者来说容易让人迷惑。
理论的学习还是为了实践。实战中如果需要用到装饰器模式,可以从模仿 Java IO 部分的装饰器模式开始。模仿是创新的开始。
标签:耦合 test buffer 文件 des 封装 角色 部分 道具
原文地址:https://www.cnblogs.com/54chensongxia/p/12467971.html