装饰者模式
写在前边的话
程序要遵循开放-闭合原则,即对扩展开放,对修改闭合。扩展又分为编译期扩展和运行期扩展。其中,继承就是编译期扩展,组合就是运行期扩展。运行期扩展要比编译期更强大.
遵循开放-闭合原则,通常会引入新的抽象层次,增加代码的复杂度而且难以理解(即,不要滥用装饰者模式)。我们需要把最有可能改变的地方应用开放-闭合原则,但那些最有可能改变,这就需要经验(对,这个说不清 )
定义
装饰者将一个对象包装起来以增强新的行为和责任
特点:
装饰者和被装饰者拥有相同的超类型(可能是抽象类也可能是接口)
可以用多个装饰类来包装一个对象,装饰类可以包装装饰类或被装饰对象
因为装饰者和被装饰者拥有相同的抽象类型,因此在任何需要原始对象(被包装)的场合,都可以用装饰过的对象来替代它。
装饰者可以在被装饰者的行为之前或之后,加上自己的附加行为,以达到特殊目的
因为对象可以在任何的时候被装饰,所以可以在运行时动态地、不限量地用你喜欢的装饰者来装饰对象
案例
星巴克咖啡
JAVA I/O
手动写一个装饰者模式
编写一个装饰者,把输入流中的所有大写都改成小写
public class LowerCaseInputStream extends FilterInputStream{
public LowerCaseInputStream(InputStream in){
super(in);
}
@Override
public int read() throws IOException {
int c = super.read();//从输入流中读取数据的下一个字节。
return (c == -1)? -1 : Character.toLowerCase((char)c);
}
@Override
public int read(byte[] b,int offset,int len) throws IOException{
//将输入流中最多 len 个数据字节读入 byte 数组
int result = super.read(b,offset,len);
for(int i = offset;i< offset + result; i++){
b[i] = (byte)Character.toLowerCase((char)b[i]);
}
return result;
}
public class InputTest {
public static void main(String[] args) throws IOException {
InputStream in = new LowerCaseInputStream(new BufferedInputStream(new FileInputStream("C:\\Users\\xingzhe\\Desktop\\partner4.sql")));
int c;
while((c = in.read()) >= 0){
System.out.print((char)c);
}
in.close();
}
}
}
装饰者模式的缺点
- 在设计中会有大量的小类,增加理解的难度。例如文件读取类,都是用来包装InputStream类的
- 有时客户端依赖于被包装类的特殊类型,而用装饰对象就无法导到这些特殊的东西,导致问题
- 一旦使用装饰者模式,不只需要实例化组件(被装饰者),还要把次组件包装到装饰者中,注意,此时可能会有多个装饰者哟。
例如,FileInputStream
//LineNumberInputStream
InputStream in = new LineNumberInputStream(
new BufferInputStream(
new FileInputStream("hello.txt")))
工厂模式和生成器模式会对3这个问题有很大帮助
疑问
- 星巴克那个例子中,超类Beverage作为装饰者(Mocha)的一个实例变量,用来记录饮料,然后用调料去装饰;而 JAVA I/O中的LowerCaseInputStream 并没有将超类InputStream作为一个变量