举例来说,如果你想得到一个带有边框的Swing类型标签,你可以子类化javax.swing.JLabel类。然而,子类化并不总是有效。当继承不能解决问题的时候,你不得不求助与其它的方式。比如,使用Decorator模式。
这篇文章解释了Decorator模式是什么,并说明什么时候应该子类化,什么时候应该采用Decorate模式。
在Java语言中关键字extends被提供来子类化(扩展)一个类。具有丰富的面向对象编程经验的程序员知道子类化的威力。通过扩展一个类,我们能够改变这个类的行为。以列表1所讲的JBorderLabel类为例,它扩展了javax.swing.JLabel类,除了多了一个边框,它和JLabel类具有相同的外观和行为。
列表 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绘它的组件的原理。 JLabel类同其它的Swing组件一样,继承至javax.swing.Jcomponent.Swing。它们都是通过调用JComponent组件的paint方法来画界面。我们可以通过重载JComponent的公开方法paint来修改一个组件画界面的行为。下面是一个JComponent的paint方法的定义。
public void paint(Graphics g)
作为paint方法的参数传进来的对象Graphics是一个绘图面板。为了优化绘图这个操作,paint方法被分割成三个具有保护(protected)属性的方法:paintComponent, paintBorder, paintChildren。paint方法调用这三个方法同时将它接受到的Graphics实例传递给这三个方法。下面是这三个函数的一个声明:
protected void paintComponent(Graphics g)
protected void paintBorder(Graphics g)
protected void paintChildren(Graphics g)
你可以通过重载这些方法来定制你自己的绘制组件的方式。
JBorderLabel类重载了javax.swing.JComponent的paintComponent方法。类JborderLabel的paintComponent方法首先调用父类的paintComponent得到一个Jlabel.它保持了自己的长和宽,通过java.awt.Graphics实例的drawRect方法画一个矩形。图1显示了一个JBorderLabel类的一个实例。正如图所示的一样,出了多了一个边框外,它和JLabel外观是一样的。
这个例子中子类化工作得相当好。我们来看看子类化不合适的案例。如果你打算让其它的组件都具有同一行为(比如:画一个边框),那么你必须做很多的子类化操作。在列表1中,子类化看起来很简单是因为例子中你仅仅需要重载一个方法。当你有太多的子类需要创建时你的代码将变得很复杂,出错的机会也增大了。(你必需要复制(reproduce)你的子类需要支持的父类的构造函数,就像JBorderLabel类一样)。在这个时候,最好的方式是使用Decorator模式。
Decorator模式
在Erich Gamma等编写的《Design Patterns : Elements of Reusable Object-Oriented Software》一书中,Decorator模式被归类为结构模式。Decorator模式提供了子类化的一个替代方案。子类化和Decorator模式的主要区别是:采用子类化,你同一个类打交道;使用Decorator模式,你可以动态的修改多个对象。当你扩展(Extend)一个类的时候,你对儿子类的改变将会影响到这个儿子类所有的实例。采用Decorator模式,你所作的改变只会影响到你打算改变的那个对象。
理解JComponent类对于书写装饰者类很重要,我们通过这个装饰者类来改变Swing组件的用户界面。在前面部分我解释了JComponent是如何画它的用户界面的,我们可以通过文档查找来了解这个类的所有的成员。我们要意识到JComponent有子组件,当JComponent被画的时候,这些子组件也将被画。
创建一个从JComponent扩展过来的Swing装饰者。这个装饰者的构造函数接受一个类型为JComponent的参数。可以传递任一一个需要改变行为的Swing对象给装饰者。这个装饰者将传进来的这个组件作为自己的子组件。并不是直接将Swing组件增加到JFrame或JPannel或其它容器,而是先将Swing组件添加到修饰者,再把修饰者增加给容器类。因为一个修饰者也是一个JComponent类型的对象,容器不能将他们区分开来。这个装饰者是这个容器的一个子组件。当容器让装饰者重画的时候,这个装饰者paint方法将被调用。
举例来说,假设你有一个JLabel类,你打算把它传给一个称之为frame1的JFrame类。使用如下相似的代码:
frame.getContentPane().add(new JLabel("a label"));
用MyDecorator来修饰JLabel的代码和它很相似,如下:(记住,MyDecorator类的构造函数应该接受一个JComponent类的输入参数)
frame.getContentPane().add(new MyDecorator(new JLabel("a label")));
这篇文章示例了两个Decorator模式的例子。第一个例子是BorderDecorator.这个类被用来修饰JComponent,以便让JComponent具有一个边框。当把一个由BorderDecorator修饰的JLabel增加到JFrame,这个JLabel看起来就像JBorderLabel的一个实例。这说明,子类化不是必须的。更好的是,你能够传递任何一个Swing组件给BorderDecorator,这些被传递的组件都会给予一个边框。在这个例子中,通过创建了一个类BorderDecorator来改变不同类型的实例的行为。
第二个例子是ResizableDecorator。这个装饰着为每一个传给它的Swing组件增加一个小按钮到左上角。当用户点击这个按钮的时候,这个组件将会最小化为这个按钮。
BorderDecorator类
我们以BorderDecorator开始。这个类表示的装饰者会为Swing组件增加一个边框。示例代码如列表2
列表2 -- the BorderDecorator class
package decorator;
import javax.swing.JComponent;
import java.awt.Graphics;
import java.awt.Color;
import java.awt.BorderLayout;
public class BorderDecorator extends JComponent {
// decorated component
protected JComponent child;
public BorderDecorator(JComponent component) {
child = component;
this.setLayout(new BorderLayout());
this.add(child);
}
public void paint(Graphics g) {
super.paint(g);
int height = this.getHeight();
int width = this.getWidth();
g.drawRect(0, 0, width - 1, height - 1);
}
}
注意,这个BorderDecorator扩展了JComponent,它的构造函数接受一个JComponet类型的参数。这个BorderDecorator类有一个类型为JComponent的属性child,它是传进来的Jcomponent对象的一个引用。
matrix开源技术经onjava授权翻译并发布.
如果你对此文章有任何看法或建议,请到Matrix论坛发表您的意见.
注明: 如果对matrix的翻译文章系列感兴趣,请点击oreilly和javaworld文章翻译计划查看详细情况
您也可以点击-disneytiger查看翻译作者的详细信息. Java, java, J2SE, j2se, J2EE, j2ee, J2ME, j2me, ejb, ejb3, JBOSS, jboss, spring, hibernate, jdo, struts, webwork, ajax, AJAX, mysql, MySQL, Oracle, Weblogic, Websphere, scjp, scjd
标签: