TA的每日心情 | 开心 2021-3-12 23:18 |
---|
签到天数: 2 天 [LV.1]初来乍到
|
java程序员知道可以通过扩展一个类来改变类的行为和扩展一个类的功能。这个行为被称为继承,它是面向对象编程的一个重要的特性。举例来说,如果你想得到一个带有边框的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(Graphicsg)
作为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方法画一个矩形。它和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对象的一个引用。 构造函数将被修饰的组件赋值给child变量,并且将这个组件作为一个子组件增加给装饰者。注意,我们使用了BorderLayout作为装饰者的布局。这意味着被增加的这个JComponent将占据这个装饰者的整个区域。
现在,让我们关注一下paint方法。它首先调用了父类的paint方法。这-步操作将画出装饰者,在第一次得到装饰者的长宽以后,我们在装饰者所在区域的边缘画一个长方形。
JBorderLabel的一个实例和一个被装饰过的JLabel对象实例从外表看没有什么不同。这说明,Decorator模式可以作为子类化的一个替代方案。你也能够使用同一个装饰者去扩展不同对象的实例的行为。从这点来看,装饰者是一个(超类)superior,因为仅仅需要创建一个类(BorderDecorator)就可以扩张不同类型的多个对象的功能。
清单3显示了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);
}
这段代码恢复修饰者尺寸大小。
Frame2显示了带有三个被修饰过的组件的JFrame。其实现代码如列表5。
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组件的可视化外观很普遍,但是它的作用不仅仅是用来改变用户界面。
【关于作者】
本系列文章由disneytiger翻译,disneytiger ,java 爱好者,Matrix j2ee翻译小组成员!可以点击
http://www.matrix.org.cn/user_view.asp?username=disneytiger查看他的信息.
function TempSave(ElementID)
{
CommentsPersistDiv.setAttribute("CommentContent",document.getElementById(ElementID).value);
CommentsPersistDiv.save("CommentXMLStore");
}
function Restore(ElementID)
{
CommentsPersistDiv.load("CommentXMLStore");
document.getElementById(ElementID).value=CommentsPersistDiv.getAttribute("CommentContent");
}
源码下载:http://file.javaxxz.com/2014/10/30/235914046.zip |
|