使用Decorator模式 Java程序员知道可以通过扩展一个类来改变类的行为和扩展一个类的功能这个行为被称为继承它是面向对象编程的一个重要的特性 举例来说如果你想得到一个带有边框的Swing类型标签你可以子类化javaxswingJLabel类然而子类化并不总是有效当继承不能解决问题的时候你不得不求助与其它的方式比如使用Decorator模式 这篇文章解释了Decorator模式是什么并说明什么时候应该子类化什么时候应该采用Decorate模式 在Java语言中关键字extends被提供来子类化(扩展)一个类具有丰富的面向对象编程经验的程序员知道子类化的威力通过扩展一个类我们能够改变这个类的行为以列表所讲的JBorderLabel类为例它扩展了javaxswingJLabel类除了多了一个边框它和JLabel类具有相同的外观和行为 the JBorderLabel class an example of subclassing package decorator; import javaawtGraphics; import javaxswingJLabel; import javaxswingIcon; public class JBorderLabel extends JLabel { public JBorderLabel() { super(); } public JBorderLabel(String text) { super(text); } public JBorderLabel(Icon image) { super(image); } public JBorderLabel(String text Icon image int horizontalAlignment) { super(text image horizontalAlignment); } public JBorderLabel(String text int horizontalAlignment) { super(text horizontalAlignment); } protected void paintComponent(Graphics g) { superpaintComponent(g); int height = thisgetHeight(); int width = thisgetWidth(); gdrawRect( width height ); } } 要理解JBorderLabel如何工作我们首先要了解Swing绘它的组件的原理 JLabel类同其它的Swing组件一样继承至javaxswingJcomponentSwing它们都是通过调用JComponent组件的paint方法来画界面我们可以通过重载JComponent的公开方法paint来修改一个组件画界面的行为下面是一个JComponent的paint方法的定义 public void paint(Graphics g) 作为paint方法的参数传进来的对象Graphics是一个绘图面板为了优化绘图这个操作paint方法被分割成三个具有保护(protected)属性的方法paintComponent paintBorder paintChildrenpaint方法调用这三个方法同时将它接受到的Graphics实例传递给这三个方法下面是这三个函数的一个声明 protected void paintComponent(Graphics g) protected void paintBorder(Graphics g) protected void paintChildren(Graphics g) 你可以通过重载这些方法来定制你自己的绘制组件的方式 JBorderLabel类重载了javaxswingJComponent的paintComponent方法类JborderLabel的paintComponent方法首先调用父类的paintComponent得到一个Jlabel它保持了自己的长和宽通过javaawtGraphics实例的drawRect方法画一个矩形图显示了一个JBorderLabel类的一个实例正如图所示的一样出了多了一个边框外它和JLabel外观是一样的 这个例子中子类化工作得相当好我们来看看子类化不合适的案例如果你打算让其它的组件都具有同一行为(比如画一个边框)那么你必须做很多的子类化操作在列表中子类化看起来很简单是因为例子中你仅仅需要重载一个方法当你有太多的子类需要创建时你的代码将变得很复杂出错的机会也增大了(你必需要复制(reproduce)你的子类需要支持的父类的构造函数就像JBorderLabel类一样)在这个时候最好的方式是使用Decorator模式 Decorator模式 在Erich Gamma等编写的《Design Patterns : Elements of Reusable ObjectOriented Software》一书中Decorator模式被归类为结构模式Decorator模式提供了子类化的一个替代方案子类化和Decorator模式的主要区别是采用子类化你同一个类打交道使用Decorator模式你可以动态的修改多个对象当你扩展(Extend)一个类的时候你对儿子类的改变将会影响到这个儿子类所有的实例采用Decorator模式你所作的改变只会影响到你打算改变的那个对象 理解JComponent类对于书写装饰者类很重要我们通过这个装饰者类来改变Swing组件的用户界面在前面部分我解释了JComponent是如何画它的用户界面的我们可以通过文档查找来了解这个类的所有的成员我们要意识到JComponent有子组件当JComponent被画的时候这些子组件也将被画 创建一个从JComponent扩展过来的Swing装饰者这个装饰者的构造函数接受一个类型为JComponent的参数可以传递任一一个需要改变行为的Swing对象给装饰者这个装饰者将传进来的这个组件作为自己的子组件并不是直接将Swing组件增加到JFrame或JPannel或其它容器而是先将Swing组件添加到修饰者再把修饰者增加给容器类因为一个修饰者也是一个JComponent类型的对象容器不能将他们区分开来这个装饰者是这个容器的一个子组件当容器让装饰者重画的时候这个装饰者paint方法将被调用 举例来说假设你有一个JLabel类你打算把它传给一个称之为frame的JFrame类使用如下相似的代码 framegetContentPane()add(new JLabel(a label));用MyDecorator来修饰JLabel的代码和它很相似如下(记住MyDecorator类的构造函数应该接受一个JComponent类的输入参数) framegetContentPane()add(new MyDecorator(new JLabel(a label))); 这篇文章示例了两个Decorator模式的例子第一个例子是BorderDecorator这个类被用来修饰JComponent以便让JComponent具有一个边框当把一个由BorderDecorator修饰的JLabel增加到JFrame这个JLabel看起来就像JBorderLabel的一个实例这说明子类化不是必须的更好的是你能够传递任何一个Swing组件给BorderDecorator这些被传递的组件都会给予一个边框在这个例子中通过创建了一个类BorderDecorator来改变不同类型的实例的行为 第二个例子是ResizableDecorator这个装饰着为每一个传给它的Swing组件增加一个小按钮到左上角当用户点击这个按钮的时候这个组件将会最小化为这个按钮 BorderDecorator类 我们以BorderDecorator开始这个类表示的装饰者会为Swing组件增加一个边框示例代码如列表 the BorderDecorator class package decorator; import javaxswingJComponent; import javaawtGraphics; import javaawtColor; import javaawtBorderLayout; public class BorderDecorator extends JComponent { // decorated component protected JComponent child; public BorderDecorator(JComponent component) { child = component; thissetLayout(new BorderLayout()); thisadd(child); } public void paint(Graphics g) { superpaint(g); int height = thisgetHeight(); int width = thisgetWidth(); gdrawRect( width height ); } } 注意这个BorderDecorator扩展了JComponent它的构造函数接受一个JComponet类型的参数这个BorderDecorator类有一个类型为JComponent的属性child它是传进来的Jcomponent对象的一个引用 构造函数将被修饰的组件赋值给child变量并且将这个组件作为一个子组件增加给装饰者注意我们使用了BorderLayout作为装饰者的布局这意味着被增加的这个JComponent将占据这个装饰者的整个区域 现在让我们关注一下paint方法它首先调用了父类的paint方法这-步操作将画出装饰者在第一次得到装饰者的长宽以后我们在装饰者所在区域的边缘画一个长方形 Figure shows a JFrame with three components: ; An instance of JBorderLabel ; A decorated JLabel ; A decorated JCheckBox Figure comparing subclassing and the Decorator pattern JBorderLabel的一个实例和一个被装饰过的JLabel对象实例从外表看没有什么不同这说明Decorator模式可以作为子类化的一个替代方案第三个组件证明你能够使用同一个装饰者去扩展不同对象的实例的行为从这点来看装饰者是一个(超类)superior因为仅仅需要创建一个类(BorderDecorator)就可以扩张不同类型的多个对象的功能 显示了图中的JFrame类的实现代码 using the BorderDecorator class package decorator; import javaawt*; import javaxswing*; import javaawtevent*; public class Frame extends JFrame { JBorderLabel label = new JBorderLabel(JLabel Subclass); BorderDecorator label = new BorderDecorator(new JLabel(Decorated JLabel)); BorderDecorator checkBox = new BorderDecorator(new JCheckBox(Decorated JCheckBox)); public Frame() { try { thissetDefaultCloseOperation(EXIT_ON_CLOSE); getContentPan |