电脑技术学习

使用Decorator模式(二)

dn001
内容: 构造函数将被修饰的组件赋值给child变量,并且将这个组件作为一个子组件增加给装饰者。注意,我们使用了BorderLayout作为装饰者的布局。这意味着被增加的这个JComponent将占据这个装饰者的整个区域。

现在,让我们关注一下paint方法。它首先调用了父类的paint方法。这-步操作将画出装饰者,在第一次得到装饰者的长宽以后,我们在装饰者所在区域的边缘画一个长方形。

Figure 1 shows a JFrame with three components:
· An instance of JBorderLabel.
· A decorated JLabel.
· A decorated JCheckBox.

IMG http://www.onjava.com/onjava/2003/02/05/graphics/figure1.png[/IMG]
Figure 1 -- comparing subclassing and the Decorator pattern
像图1所显示的那样,JBorderLabel的一个实例和一个被装饰过的JLabel对象实例从外表看没有什么不同。这说明,Decorator模式可以作为子类化的一个替代方案。第三个组件证明,你能够使用同一个装饰者去扩展不同对象的实例的行为。从这点来看,装饰者是一个(超类)superior,因为仅仅需要创建一个类(BorderDecorator)就可以扩张不同类型的多个对象的功能。

清单3显示了图1中的JFrame类的实现代码。
列表 3 -- using the BorderDecorator class
package decorator;

import java.awt.*;
import javax.swing.*;
import java.awt.event.*;

public class Frame1 extends JFrame {

JBorderLabel label1 =
new JBorderLabel("JLabel Subclass");

BorderDecorator label2 =
new BorderDecorator(new JLabel("Decorated JLabel"));

BorderDecorator checkBox1 =
new BorderDecorator(new JCheckBox("Decorated JCheckBox"));

public Frame1() {
try {
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
getContentPane().setLayout(null);
label1.setBounds(new Rectangle(10, 10, 120, 25));
this.getContentPane().add(label1, null);
label2.setBounds(new Rectangle(10, 60, 120, 25));
this.getContentPane().add(label2, null);
checkBox1.setBounds(new Rectangle(10, 110, 160, 25));
this.getContentPane().add(checkBox1, null);
}
catch(Exception e) {
e.printStackTrace();
}
}

public static void main(String[] args) {
Frame1 frame1 = new Frame1();
frame1.setBounds(0, 0, 200, 200);
frame1.setVisible(true);
}
}

ResizableDecorator类
ResizableDecorator是装饰者的另一种类型。它没有重载父类的paint方法;它增加一个按钮,当点击按钮的时候,它可以改变或恢复自身的大小。列表4显示了ResizableDecorator类的实现代码。

列表 4 -- the ResizableDecorator class
package decorator;

import javax.swing.*;
import java.awt.Graphics;
import java.awt.Color;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.Rectangle;

public class ResizableDecorator extends JComponent {

// decorated component
protected JComponent child;
private JButton button = new JButton();
boolean minimum = false;
private Rectangle r;

public ResizableDecorator(JComponent component) {
child = component;
this.setLayout(new BorderLayout());
this.add(child);
child.setLayout(null);
button.setBounds(0, 0, 8, 8);
child.add(button);
button.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(ActionEvent e) {
button_actionPerformed(e);
}
});
}

void button_actionPerformed(ActionEvent e) {
if (minimum) {
this.setBounds(r);
}
else {
r = this.getBounds();
this.setBounds(r.x, r.y, 8, 8);
}
minimum = !minimum;
}
}

需要注意的是,这个装饰者同样扩展自JComponent,它的构造函数也接受一个JComponent类型的参数。除了定义了一个child变量来引用这个被修饰的组件外,它也定义了一个类型为JButton的变量。
构造函数代码的头三行实现的功能是:将被修饰的组件作为儿子组件增加到修饰者。
child = component;
this.setLayout(new BorderLayout());
this.add(child);

将一个Jbutton组件作为子组件增加给被修饰的组件。这个子组件的布局为空。JButton组件的尺寸是8*8,它被增加到这个被修饰组件的左上角。
child.setLayout(null);
button.setBounds(0, 0, 8, 8);
child.add(button);

最后,我们为JButton增加一个监听对象。
button.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(ActionEvent e) {
button_actionPerformed(e);
}
});

当被增加到一个容器的时候,这个使用ResizableDecorator修饰的组件会在它的左上角出现一个小按钮。小按钮被点击的时候,会执行button_actionPerformed方法里面的代码。首先,它检查变量minimum的值。这个布尔变量被用来标识装饰者是否处于最小化。最初,变量的值为false,这个时候else部分的代码将被执行。
else {
r = this.getBounds();
this.setBounds(r.x, r.y, 8, 8);
}

它将当前的尺寸赋值给变量r,r是一个Rectangle类型并且将它的范围设置为8 * 8。然后对minimum取反。
minimum = !minimum;

当按钮被第二次点击的时候,minimum的值变为true。这个时候,if语句块的代码将被执行。
if (minimum) {
this.setBounds(r);
}

这段代码恢复修饰者尺寸大小。
图2显示了带有三个被修饰过的组件的JFrame。其实现代码如列表5。
IMG http://www.onjava.com/onjava/2003/02/05/graphics/figure2.png[/IMG]
图 2 – 三个用ResizableDecorator类修饰的组件

列表 5 -- using the ResizableDecorator class
package decorator;

import java.awt.*;
import javax.swing.*;
import java.awt.event.*;

public class Frame2 extends JFrame {
ResizableDecorator label1 =
new ResizableDecorator(new JLabel(" Label1"));
ResizableDecorator button1 =
new ResizableDecorator(new JButton("Button"));
BorderDecorator label2 =
new BorderDecorator(new ResizableDecorator(
new JLabel(" doubly decorated")
));

public Frame2() {
try {
jbInit();
}
catch(Exception e) {
e.printStackTrace();
}
}

public static void main(String[] args) {
Frame2 frame = new Frame2();
frame.setBounds(0, 0, 200, 200);
frame.setVisible(true);
}

private void jbInit() throws Exception {
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
this.getContentPane().setLayout(null);
label1.setBounds(new Rectangle(10, 10, 120, 25));
label2.setBounds(new Rectangle(10, 60, 120, 25));
button1.setBounds(new Rectangle(10, 110, 120, 25));
this.getContentPane().add(label1, null);
this.getContentPane().add(label2, null);
this.getContentPane().add(button1, null);
}
}

你可以使用一个或多个修饰者来修饰一个Jcomponent对象。就像列表5中的label2一样。如果你需要使用多个修饰者,不妨考虑一下提供一个抽象类Decorator,所有的修饰者都从这个类继承。采用这种方式,你就可以把多个修饰着公用的代码放到抽象类中实现。

总结:
这片文章将子类化和Decorator模式进行了比较,给予了两个基于Swing的采用Decorator模式的例子。虽然,使用Decorator模式改变一个Swing组件的可视化外观很普遍,但是它的作用不仅仅是用来改变用户界面。

作者介绍:
Budi Kurniawan is an IT consultant specializing in Internet and object-oriented programming, and has taught both Microsoft and Java technologies

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

标签: