一、功能描述
装饰者模式可以在运行时期动态的扩展一个类对象的功能,向一个类对象而不是类增加额外的功能。装饰者模式可以看做是继承的替代,两者的不同处是:继承是在编译期间增加类(不是对象)的功能,而装饰者模式则是在运行时动态的添加功能到单个对象。
二、实现描述
动态的向一个对象添加功能是由称之为装饰者的类来完成的,在创建这个装饰者的类的过程中,需要将原始被包装对象当做参数传递给装饰者的构造器。然后由这个装饰器利用原始类提供的基本功能进而实现额外的功能,需要注意的是装饰器的类必须与原始类具有相同接口。
三、实现过程
这里用一个例子来描述实现过程。假设我们有一个实现了Window接口的窗口类,现在我们需要对这个窗口增加一个滚动功能,也就是需要在窗口上增加水平跟竖直滚动条,在Window接口中并没有增加滚动条功能的方法,要实现这个功能,我们可以写一个这个窗口类的子类ScrollingWindow,在这个子类中将窗口滚动功能加上,或者我们可以写一个具有滚动功能的装饰器类ScrollingWindowDecorator,然后将这个装饰器类动态的将这个功能增加到一个已存在的Window对象中。这么看来,这两种方式都可以。
如果我们想增加边界功能到 Window上并且Window没有提供增加边界的方法 。这时,对于 ScrollingWindow这种实现方式来说,我们想增加边界到Window上,我们必须创建两个子类分别是:WindowWithBorder 和 ScrollingWindowWithBorder。很明显,如果我们还想增加功能的时候这个问题就会引起类爆炸的问题。而对于装饰者这种实现方式,我们仅仅增加一个具有边界功能的装饰类BorderedWindowDecorator 即可。这样运行时,我们可以用 BorderedWindowDecorator或者 ScrollingWindowDecorator来装饰已存在的窗口对象或者两个装饰器同时使用。
四、具体实现
参考上面的例子,一步步实现这个装饰器模式
- 首先定义一个Window接口,提供一些基本的方法
// the Window interface
interface Window {
public void draw(); // draws the Window
public String getDescription(); // returns a description of the Window
}
- 提供这个接口的简单实现
// implementation of a simple Window without any scrollbars
class SimpleWindow implements Window {
public void draw() {
// draw window
}
public String getDescription() {
return "simple window";
}
}
- 定义装饰器的抽象类,所有的装饰器都会继承该抽象类,这个抽象类需要实现Window接口。
// abstract decorator class - note that it implements Window
abstract class WindowDecorator implements Window {
protected Window decoratedWindow; // the Window being decorated
public WindowDecorator (Window decoratedWindow) {
this.decoratedWindow = decoratedWindow;
}
}
- 实现竖直滚动条装饰器
// the first concrete decorator which adds vertical scrollbar functionality
class VerticalScrollBarDecorator extends WindowDecorator {
public VerticalScrollBarDecorator (Window decoratedWindow) {
super(decoratedWindow);
}
public void draw() {
drawVerticalScrollBar();
decoratedWindow.draw();
}
private void drawVerticalScrollBar() {
// draw the vertical scrollbar
}
public String getDescription() {
return decoratedWindow.getDescription() + ", including vertical scrollbars";
}
}
- 实现水平滚动条装饰器
// the second concrete decorator which adds horizontal scrollbar functionality
class HorizontalScrollBarDecorator extends WindowDecorator {
public HorizontalScrollBarDecorator (Window decoratedWindow) {
super(decoratedWindow);
}
public void draw() {
drawHorizontalScrollBar();
decoratedWindow.draw();
}
private void drawHorizontalScrollBar() {
// draw the horizontal scrollbar
}
public String getDescription() {
return decoratedWindow.getDescription() + ", including horizontal scrollbars";
}
}
- 测试代码
public class DecoratedWindowTest {
public static void main(String[] args) {
// create a decorated Window with horizontal and vertical scrollbars
Window decoratedWindow = new HorizontalScrollBarDecorator (
new VerticalScrollBarDecorator(new SimpleWindow()));
// print the Window's description
System.out.println(decoratedWindow.getDescription());
}
}
五、组成结构
装饰者模式的组织结构可以由下面的图来说明:
下面分别介绍图中元素:
- Component
为被装饰的对象定义的接口。
- ConcreteComponent
被装饰对象的接口实现类。
- Decorator
维持一个Component对象的引用,并与Component接口保持一致(继承这个接口)
- ConcreteDecorator
代表固定功能的装饰器类。
六、java类库中使用装饰者模式的例子
在java类库中,大量的用到了装饰者模式,其中最突出的就是在java.io包中了。一开始我们学习java的io流的时候,可能看到这么多的类会一头雾水,但是,只要理解了装饰者模式,那么对于io包中的类便会一目了然了。比如说:FileInputStream类,就是一个被装饰的对象类,我们可以用BufferedInputStream去装饰它,也可以用LineNumberInputStream去装饰它,当然也可以两者一起来装饰。BufferedInputStream和LineNumberInputStream都继承自FilterInputStream,而FilterInputStream是一个抽象的装饰类。
下面这个图能更好的理解java的io包中的类的组织形式: