《一》
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--使用继承来扩展一个类的功能
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.JComponent。Swing通过调用JComponent类中的paint方法来绘制界面。我们可以通过覆写public paint方法来适应我们的界面要求。下面是paint方法在JComponent类中的签名
Graphics对象代表组件的绘制界面,为了优化绘制,paint方法被分成了3个权限为protected的方法:paintComponent, paintBorder, 和 paintChildren。paint调用了这些方法,下面是这3个方法
的签名
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对象给它,可以改变该对象的表现。
在该装饰器类中,传入的组件作为子组件,把子组件添加进装饰器类中,之后就可把装饰器类对象添加到JFrame或JPanel中了。因为一个装饰器类也是一个组件类(继承自JComponent),容器不会知道差别。现在,装饰器类成了容器(JFrame或Jpanel容器)的一个子组件。装饰器类的draw方法会在需要时被调用(这一句原文是这样的:When the container gives the decorator a chance to draw itself, the decorator's paint method will be invoked.)。
举个例子,假设你在JFrame的实例frame中添加一个JLabel,用诸如下面的代码即可:
为了用装饰器类来装饰你的JLabel,假设装饰器类名为MyDecorator,则要写的代码是相似的(记住MyDecorator的构造方法是以Jcomponent对象作为参数)
参见第二篇(⊙0⊙)