装饰器模式
1.定义
-
装饰模式:是指在不改变原有对象的基础之上,将功能附加到对象上,提供了比继承更有弹性的替代方案。
-
装饰模式中的角色:
- 抽象构件(Component)角色:给出一个抽象接口,以规范准备接受附加职责的对象。
- 具体构件(ConcreteComponent)角色:定义一个将要接收附加责任的类。
- 装饰(Decorator)角色:持有一个构件(Component)对象的实例,并定义一个与抽象构件接口一致的接口。
- 具体装饰(ConcreteDecorator)角色:负责给构件对象添加附加职责。
-
装饰模式的简单实现:
package cn.sun.code.six.decorator; abstract class Component { public abstract void operation(); } class ConcreteComponent extends Component { @Override public void operation() { System.out.println("具体对象的操作"); } } abstract class Decorator extends Component{ protected Component component; public void setComponent(Component component) { this.component = component; } // 重写Operation(),实际执行的是Component的Operation() @Override public void operation() { component.operation(); } } class ConcreteDectatorA extends Decorator { // 本类独有的功能,以区别于ConcreteDectatorB private String addedState; // 首先运行原Component的Operation(),再执行本类的功能,如addState,相当于对原Component进行了装饰 @Override public void operation() { super.operation(); addedState = "New State"; System.out.println("具体装饰对象A的操作"); } } class ConcreteDectatorB extends Decorator { @Override public void operation() { super.operation(); addedBehavior(); System.out.println("具体装饰对象B的操作"); } // 本类独有的方式,以区别于ConcreteDectatorA private void addedBehavior(){ } public static void main(String[] args) { ConcreteComponent c = new ConcreteComponent(); ConcreteDectatorA d1 = new ConcreteDectatorA(); ConcreteDectatorB d2 = new ConcreteDectatorB(); // 装饰的方法是:首先用ConcreteComponent实例化对象c,然后用ConcreteDecoratorA的 // 实例化对象d1来包装c,再用COncreteDecoratorB的对象d2来包装d1,最终执行d2的Operation() d1.setComponent(c); d2.setComponent(d1); d2.operation(); } } 具体对象的操作 具体装饰对象A的操作 具体装饰对象B的操作
2.为什么使用装饰模式
- 其实装饰模式实现的方式通过向下扩展子类一样可以实现相同的功能,那么之所以产生这种设计模式就应该是它比生成子类的方式更加有效。
- 装饰模式的优点:
- 装饰模式和继承关系的目的都是要扩展对象的功能,但是装饰模式可以提供比继承更多的灵活性。装饰模式允许系统动态决定“贴上“一个需要的”装饰”。继承关系则不同,继承关系是静态的,它在系统运行前就决定了。
- 通过使用不同的具体装饰类以及这些装饰类的排列组合,设计师可以创造出很多不同的行为。
- 装饰模式的缺点:
- 由于使用装饰模式,可以比使用继承关系需要较少数目的类。使用较少的类,当然使设计比较易于进行。但是,在另一方面,使用装饰模式会产生比使用继承关系更多的对象。更多的对象会使得查错变得困难,特别是这些对象看上去都很相像。
3.装饰模式在Java中的应用
-
装饰模式在Java语言中最著名的应用莫过于Java I/O标准库的设计了。
-
选取字节输入流InputStream的类结构体系:
-
InputStream是一个抽象构件角色:
public abstract class InputStream implements Closeable {
// SKIP_BUFFER_SIZE is used to determine the size of skipBuffer
private static final int SKIP_BUFFER_SIZE = 2048;
// skipBuffer is initialized in skip(long), if needed.
private static byte[] skipBuffer;
...
}
- ByteArrayInputStream、FileInputStream、ObjectInputStream、PipedInputStream都是具体构建角色,比如FileInputStream,它的声明是:
public
class FileInputStream extends InputStream
{
/* File Descriptor - handle to the open file */
private FileDescriptor fd;
private FileChannel channel = null;
...
}
- FilterInputStream无疑就是一个装饰角色,因为FilterInputStream实现了InputStream内的所有抽象方法并且持有一个InputStream的引用:
public
class FilterInputStream extends InputStream {
/**
* The input stream to be filtered.
*/
protected volatile InputStream in;
...
}
- 具体装饰角色就是InflaterInputStream、BufferedInputStream、DataInputStream,比如BufferedInputStream的声明就是:
public
class BufferedInputStream extends FilterInputStream {
private static int defaultBufferSize = 8192;
/**
* The internal buffer array where the data is stored. When necessary,
* it may be replaced by another array of
* a different size.
*/
protected volatile byte buf[];
...
}
4.半透明装饰模式与全透明装饰模式
- 全透明装饰模式:
- 装饰后的类有着和抽象构件角色中同样的接口方法
- 半透明装饰模式:
- 装饰后的类未必有和抽象构件角色同样的接口方法,它可以有自己扩展的方法。
- 全透明的装饰模式就是理想的装饰模式,要求具体构件角色、装饰角色的接口抽象构件角色的接口完全一致。
装饰模式与适配器模式的区别
-
适配器模式也是一种包装模式,它们的区别如下:
- 适配器模式的意义是将一个接口转换称为另一个接口,它的目的是通过包装就有接口以提供新的、需要的接口
- 装饰器模式不需要改变被装饰对象的接口,而是要保持原有对象的接口,通过装饰对象持有抽象对象来增强抽象对象功能。