Decorator是用于装饰一个事物(或人)的另一个事物(或人)一个Decorator直接改变被装饰对象的职责或特征但是不能改变被装饰对象的自有属性
从我们的专业角度来讨论一些存在的实例
JScrollPane可以装饰JComponent的视图部分JComponent本身并不会被改变但是增加了一个新的属性(可滚动)
BufferedInputStream是InputStream的装饰子本身BufferedInputStream就是一个InputStream但是它更快因为提供了对数据的缓存
考虑一下DebugButton它与JButton一样但是它在被点击时可以向日志文件添加消息DebugButton是JButton的装饰子因为它直接改变了JButton但并没有改变它的自有属性
再又如ScrollOverButton它增加了一个鼠标滑过的行为当鼠标移出时它是平的当鼠标经过时它具有一个凸起的边框很显然ScrollOverButton也是JButton的装饰子现在我们知道Decorator可能有三种不同的实现
继承(Inheritance)
封装(Wrapper)
外挂(External)
本文将讨论每一个实现模型以及它们的优缺点
继承
对于开发人员而言最直观的Decorator实现就是写一个派生类它继承自被装饰类并赋于新的职责新的职责可以是通过增加方法或是修改已有方法来实现
public class DebugButton extends JButton
{
public DebugButton()
{
addActionListener(new ActionListener()
{
Systemoutprintln(debug message);
});
}
}
此外我们也可以用相同的方式来实现ScrollOverButton不是增加ActionListener而是增加MouseListener在MouseListener回调方法中改变JButton的边框当mouseEntered()被调用时将边框从EmpetyBorder变为RaisedBevelBorder而当mouseExited()方法被调用时再将边框从RaisedBevelBorder恢复成EmpetyBorder
对于BufferedInputStream同样实现也是非常简单的修改每个读数据的方法让它从内存缓沖区来读取数据如果缓沖区是空的它可以通过superread()方法来获取数据并填充缓沖区JScrollPane要实现起来就有点复杂下面我将讨论为什么它会比较难以用继承的方式来实现
讨论一下继承方式实现Decorator模式的优点与缺点
优点
我们几乎可以用这个方式实现所有的Decorator
使用继承方式实现Decorator模式可以保留被装饰类的原始类型这一点是非常重要的用继承方式我们仍可以使用被装饰类的在被装饰之前的类型例如我们可以在我们的应用程序中使用crollOverButton代替JButton但是JScrollPane就不能代替包含在它内部的对象
缺点
用继承的方式仍不够直接设想一下我们实现了ScrollOverButton和DebugButton但是我们又需要实现一个既有ScrollOverButton特点又有DebugButton行为的按钮怎么办?用继承方式我们唯一的选择就是再派生出一个ScrollOverDebugButton类
如果我们有了ScrollOverDebugButton的实现那么是否还需要继续保留ScrollOverButton或DebugButton实现?因为我们可以为ScrollOverDebugButton增加两对方法来打开或关闭debug或scrollover的行为
public void setDebug(boolean b);
public boolean isDebug();
public void setScrollOver(boolean b);
public boolean isScrollOver();
再进一步考虑如果将来我们有更多的装饰功能增加新的UUUn个行为我们是不是要写一个类叫UUUnButton?它是不是要包括n个这样的方法
public void setU(boolean b);
public boolean getU;();
每增加一个新的行为(Un+)给装饰器就需要增加两个新的方法并要修改这个装饰器的代码实现这明显与面向对象的思想相悖可能会产生严重的后果(注意javaxswingJButton就是这样实现的)
多数可视化对象的行为是由风格参数来指定的而风格的改变是不可预知的当风格发生了改变我们不得不调整自己的改变正如上面所述使用继承的方式可能需要改变实现的代码
要保证被装饰类的原始类型也不是一件容易的事我们需要重载每个构造子有时甚至是静态方式尽管这不困难但总是相当麻烦的一件事
用继承方式来实现Decorator模式并不象我们先前想像的那么简单许多时候我们并不知道将来我们需要哪一些装饰器结果是使用继承方式的Decorator在扩展性方面相当困难并且与面向对象的原则会产生沖突