在表中显示数据时,通常会运用对象适配器。Swing提供了JTable控件用以显示表。显然,该控件的设计者并不知道你所要显示的数据。它并没有硬将数据接口塞到控件中,而是定义了TableModel接口。JTable的实现使用了该接口。然后,你可以提供一个适配器,将数据转换为TableModel,如图3.7所示。

图3.7 Swing组件中的JTable类可以将实现了TableModel的数据显示到图形界面的表中
TableModel定义了许多给出默认实现的方法。幸运的是,JDK提供了抽象类的机制,它可以为TableModel中与特定领域逻辑有关的方法提供默认实现。图3.8展现了该类的设计。

图3.8 AbstractTableModel类提供了定义在TableModel接口中大多数方法的实现
假设需要使用Swing的用户界面在表中显示几个火箭。如图3.9所示,可以创建RocketTableModel类将这组火箭适配为TableModel所期待的接口。

图3.9 RocketTableModel类将TableModel接口适配为Oozinoz领域对象Rocket类
RocketTableModel类必须继承自AbstractTableModel,因为后者是类而不是接口。无论何时,只要我们需要使用的抽象类对要适配的接口提供支持,就必须使用对象适配器方式译注[1]。不能使用类适配器的第二个原因是RocketTableModel并非Rocket的子类型。当适配类必须从多个对象处获得相关信息时,通常就应该使用对象的适配器。
注意区分:类的适配器继承自现有的类,同时实现目标接口;对象适配器继承自目标类,同时引用现有的类。
一旦创建了RocketTableModel类,就很容易在Swing的JTable对象中显示相关信息,如图3.10所示。

图3.10 填充了火箭数据信息的JTable实例
packageapp.adapter;
importjavax.swing.table.*;
importcom.oozinoz.firework.Rocket;
public class RocketTableModel extends AbstractTableModel {
protected Rocket[] rockets;
protected String[] columnNames =
new String[] { "Name", "Price", "Apogee" };
publicRocketTableModel(Rocket[] rockets) {
this.rockets = rockets;
}
publicintgetColumnCount() {
// Challenge!
}
public String getColumnName(inti) {
// Challenge!
}
publicintgetRowCount() {
// Challenge!
}
public Object getValueAt(int row, int col) {
// Challenge!
}
}
挑战3.5
完成RocketTableModel方法中的代码,使其能够将Rocket对象数组适配为TableModel。
答案参见第301页
要实现图3.10所示的显示结果,可以创建一组rocket对象,并将其放到数组中,然后再根据该数组创建RocketTableModel实例,并使用Swing的类显示表的信息。ShowRocketTable类给出了这一例子的实现:
packageapp.adapter;
importjava.awt.Component;
importjava.awt.Font;
importjavax.swing.*;
importcom.oozinoz.firework.Rocket;
importcom.oozinoz.utility.Dollars;
public class ShowRocketTable {
public static void main(String[] args) {
setFonts();
JTable table = new JTable(getRocketTable());
table.setRowHeight(36);
JScrollPane pane = new JScrollPane(table);
pane.setPreferredSize(
newjava.awt.Dimension(300, 100));
display(pane, " Rockets");
}
public static void display(Component c, String title) {
JFrame frame = new JFrame(title);
frame.getContentPane().add(c);
frame.setDefaultCloseOperation(
JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
private static RocketTableModelgetRocketTable() {
Rocket r1 = new Rocket(
"Shooter", 1.0, new Dollars(3.95), 50.0, 4.5);
Rocket r2 = new Rocket(
"Orbit", 2.0, new Dollars(29.03), 5000, 3.2);
return new RocketTableModel(new Rocket[] { r1, r2 });
}
private static void setFonts() {
Font font = new Font("Dialog", Font.PLAIN, 18);
UIManager.put("Table.font", font);
UIManager.put("TableHeader.font", font);
}
}
只需要不到20行代码,ShowRocketTable就实现了在图形化用户界面框架中生成表组件的功能。如果不使用适配器模式,可能需要上千行代码。JTable类几乎可以处理显示表数据的各种功能,但它却无法事先知道你所要显示的数据。若要提供它所需要的数据,就应该求助于适配器模式。为了使用JTable,可以实现JTable所期待的TableModel接口,提供希望显示的数据。
译注[1]:这里的假设前提是适配器对象必须要使用抽象类,因为抽象类提供了适配器对象需要的部分实现,而该抽象类又实现了客户端期待的接口,这就要求适配器对象必须继承抽象类。而同时,适配器对象还需要重用第三方对象(即目标对象)。重用的方式只能是继承或组合方式。由于Java是单继承语言,在已经继承了抽象类的情况下,无法再使用类的继承方式,因此只能将目标对象(这里即Rocket对象)以组合的方式传给适配器对象。