Using the Decorator Pattern:使用装饰模式<一>

作者在 2011-11-19 12:43:28 发布以下内容
本文由尤慕翻译自http://onjava.com/pub/a/onjava/2003/02/05/decorator.html,
原作者为Budi Kurniawan,转载请保留此条说明。

《一》

Java程序员知道通过继承一个类可以改变或扩展这个类的表现或功能。我们把这叫“inheritance”(继承),它是面向对象编程的一个重要特性。例如,如果你需要一个绘有边框的Swing标签(Jlabel),你可以写一个javax.swing.JLabel的子类。可是通过写子类来满足需求并不总是合适。使用继承在有些时候是不现实的,这时你不得不寻求其它的方式,比如使用“Decorator pattern”(装饰模式)。

本文阐述了装饰模式,并讲解了分别在什么情况下使用继承和装饰模式。文中所用的java源码放在了decorator包中,点击http://down.bccn.net/1488.html可以下载。

 java语言提供extends关键字用来写一个类的子类。对面向对象编程有充分了解的人都应该知道继承的强大。通过继承一个类,你可以改变它原有的表现。比如,在例子1中使用的JBorderLabel类。该类继承自javax.swing.Jlabel,除了拥有边框外,该类和JLabel的表现完全相同。

例子1--使用继承来扩展一个类的功能

//Listing 1 -- the JBorderLabel class, an example of subclassing

package decorator;



import java.awt.Graphics;

import javax.swing.JLabel;

import javax.swing.Icon;



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) {

      super.paintComponent(g);

      int height = this.getHeight();

      int width = this.getWidth();

      g.drawRect(0, 0, width - 1, height - 1);

   }

}

 

要理解JBorderLabel类是如何工作的,需要了解Swing组件的绘制机制。

像其它Swing组件一样,JLabel派生自javax.swing.JComponentSwing通过调用JComponent类中的paint方法来绘制界面。我们可以通过覆写public paint方法来适应我们的界面要求。下面是paint方法在JComponent类中的签名

public void paint(Graphics g)

Graphics对象代表组件的绘制界面,为了优化绘制,paint方法被分成了3个权限为protected的方法:paintComponent, paintBorder, paintChildrenpaint调用了这些方法,下面是这3个方法

的签名

protected void paintComponent(Graphics g)

protected void paintBorder(Graphics g)

protected void paintChildren(Graphics g)

覆写这三个方法中的任意一个或全部的方法都可以完成对Swing组件的自定义绘制--甚至可以覆写paint

方法。

BorderLabel类覆写了javax.swing.JComponent类中的paintComponent方法,覆写后的方法首先调用了父类的paintComponent方法,效果上是生成一个标签。然后获得该标签的高度和宽度,通过

java.awt.Graphics的实例用drawRect方法来绘制一个矩形边框。图1(在下一篇可以看到)示例了JBorderLabel类,我们把它放在在了一个JFrame中。正如你所看到的,它看上去像一个标签,只是多了一个边框罢了。

尽管通过写子类在这里工作得很好,在有些情况下却不合适。如果你想要这种表现(例如,绘制一个边框)对于其它组件仍然可行,你不得不写很多子类(针对不同的组件写不同的子类)。

在例子1中写一个子类似乎很简单因为你只需覆写一个方法。但是,当你的程序越来越复杂时,太多的子类将会带来维护上的困难,导致你的程序易于出错。(为了支持父类的构造方法,你必须在子类的构造方法中对其进行重构建,就像JBorderLabel所做的一样,)。在这种情况下,使用装饰模式显得更加必要。

 

The Decorator Pattern装饰模式

Erich Gamma et al在其所写的《Design Patterns: Elements of Reusable Object-Oriented Software》一书中,把装饰模式分类在结构化模式(structural pattern)中。这种模式给了继承一个灵活的替补选择。装饰和继承(subclassing,译为继承应该可以吧@_@)的主要不同之处在于:通过继承,你使用子类完成工作;而通过装饰,你可以动态地修改对象。当我们在继承一个类时,你对子类所做的变动会影响所有该子类的实例。而使用装饰模式,你可以方便的只对每个单一的组件进行变更。

在写一个装饰器类来对Swing组件的界面进行修改时,了解JComponent类是必要的。在前例中我已经讲述了JComponent绘制界面的方式,你可以查API文档理解该类。注意JComponent类允许包含子组件,在绘制JComponent时这些子组件同时被绘制。

我们来创建一个Swing装饰器类,该类继承自JComponent,它的构造方法接收JComponent对象作为参数。传递一个Swing对象给它,可以改变该对象的表现。

在该装饰器类中,传入的组件作为子组件,把子组件添加进装饰器类中,之后就可把装饰器类对象添加到JFrameJPanel中了。因为一个装饰器类也是一个组件类(继承自JComponent),容器不会知道差别。现在,装饰器类成了容器(JFrameJpanel容器)的一个子组件。装饰器类的draw方法会在需要时被调用(这一句原文是这样的:When the container gives the decorator a chance to draw itself, the decorator's paint method will be invoked.

 

举个例子,假设你在JFrame的实例frame中添加一个JLabel,用诸如下面的代码即可:

frame.getContentPane().add(new JLabel("a label"));

为了用装饰器类来装饰你的JLabel,假设装饰器类名为MyDecorator,则要写的代码是相似的(记住MyDecorator的构造方法是以Jcomponent对象作为参数)

frame.getContentPane().add(new MyDecorator(new JLabel("a label")));
 

参见第二篇(⊙0⊙)

 

 

设计模式 | 阅读 1314 次
文章评论,共0条
游客请输入验证码
浏览70865次
最新评论