Java资源网

| JAVA基础 | 环境配置 | JDBC | 线程技术 | Socket编程 | JavaMail | JAVA与XML | 设计模式 | 技术新闻 | Java认证 | 程序人生 软件下载
| JSP&Servlet | Spring | Struts | Hibernate | JBuilder | Eclipse | WebService | EJB技术 | J2ME开发 | 应用服务器 | JXTA | Ajax
Articles search文章搜索
   关键字:
   类 别:
       
New download 最新下载
· [组件]HTML Parser 1.5
· [教程]WebSphere Studio应用教程
· [组件]JDom 1.0
· [工具]Junit3.8.1
· [教程]EJB编程及J2EE系统架构和设计
· [教程]EJB教程
· [教程]J2EE Tutorial中文版
· [教程]Java编程思想2(英文)
· [教程]java编程思想(完整版)
· [教程]Java网络编程
New articles 最新文章
· 设计移动 Web 服务
· 解析XML的时候完全忽略DTD
· 理解XML Schema XML Schema 初步
· 标签库的深入研究
· 提升JSP应用程序的七大绝招
· 如何使用JDOM对XML文件进行操作
· 处理XML字符串中特殊字符
· 利用Digester把XML转换成为Java对象
· 使用WebService 和RMI远程协作
· 使用Axis开发Web Service程序
Articles top 热门文章
· Eclipse基础--plugin插件安装(6644)
· eclipse+tomcat+lomboz的安装配置说明(4774)
· Java程序员就业前景(4584)
· Windows下JAVA环境变量的设置祥解(3788)
· Tomcat下JSP、Servlet和JavaBean环境的配置(3716)
· 使用links方式安装Eclipse插件(3698)
· 一个老程序员的心理话(3533)
· linux下jdk的安装与配置(3459)
· 初学者入门:Structs中基本配置入门(3334)
· Eclipse 运行命令行参数大全(3084)
您的位置:首页>>设计模式>>Decoratenbsp;yournbsp;Javanbsp;codenbsp;(2)nbsp;--nbsp;Anbsp;looknbsp;atnbsp;thenbsp;Decoratornbsp;d
Decoratenbsp;yournbsp;Javanbsp;codenbsp;(2)nbsp;--nbsp;Anbsp;looknbsp;atnbsp;thenbsp;Decoratornbsp;d
2007-04-13   来源:www.javaresearch.org  作者:未知

Sort and filter decorators for Swing tables

The Decorator pattern excels at attaching functionality, such as sorting and filtering, to Swing tables. In fact, the Decorator pattern is flexible enough that we can attach functionality to any Swing table at runtime. Before we can discuss how decorators enhance Swing tables in detail, we must have a basic understanding of Swing tables; so, let's take a short detour. 

Swing tables

Swing components are implemented with the Model-View-Controller (MVC) design pattern. Each component consists of a model that maintains data, views that display the data, and controllers that react to events. For example, Swing tables, instances of JTable, are commonly created with an explicit model. Example 3 lists an application that does just that. (Try to picture the table in Example 3 before viewing it in Figure 5.) 

Example 3. Create a Swing table

 
  1. import javax.swing.*;
  2. import javax.swing.table.*;
  3. public class Test extends JFrame {
  4.    public static void main(String args[]) {
  5.       Test frame = new Test();
  6.       frame.setTitle("Tables and Models");
  7.       frame.setBounds(300, 300, 450, 300);
  8.       frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
  9.       frame.show();
  10.    }
  11.    public Test() {
  12.       TableModel model = new TestModel();   
  13.       getContentPane().add(new JScrollPane(new JTable(model)));
  14.    }
  15.    private static class TestModel extends AbstractTableModel {
  16.       final int rows = 100, cols = 10;
  17.       public int    getRowCount()    { return rows; }
  18.       public int    getColumnCount() { return cols; }
  19.       public Object getValueAt(int row, int col) {
  20.          return "(" + row + "," + col + ")";
  21.       }
  22.    }
  23. }

The application creates a table with a model. That table is placed in a scrollpane and added to the frame's content pane. Figure 5 shows the application listed in Example 3. 


[i]Figure 5. A simple Swing table[/i] 

The application shown above creates a table model with 100 rows and 10 columns. When asked for a cell value, the model creates a string that represents the cell's row and column. The application uses that model to construct a Swing table (an instance of JTable). Table models produce a table's data, in addition to metadata such as the number of rows and columns. 

It's important to understand that Swing tables possess models that maintain a table's data. Table models, as evidenced in Example 3, do not have to actually store any data -- they must produce a value for a given row and column. 

A sort decorator

With a basic understanding of the Decorator pattern and Swing tables, we can now implement a table sort decorator. First, we'll see an application that uses the decorator, followed by a discussion of the decorator's implementation. 

The application shown in Figure 6 contains a table with two columns: one for items and another for price. You can sort columns by clicking on column headers. When the application starts, nothing is sorted, as evidenced by the status bar in the bottom of the left picture in Figure 6. I took the middle picture in Figure 6 after I clicked the Item column header. I took the the right picture after I clicked the Price/Lb. column header.
 

[i]Figure 6. A sorting decorator. Click on thumbnail to view full-size image[/i] 

The application shown in Figure 6 replaces the table's model with a sort decorator. That application is partially listed in Example 4. (You can download the full source code from the Resources section below.) 

Example 4. Sorting a Swing table 


  1. import java.awt.*;
  2. import java.awt.event.*;
  3. import java.util.Locale;
  4. import java.util.ResourceBundle;
  5. import javax.swing.*;
  6. import javax.swing.event.*;
  7. import javax.swing.table.*;
  8. public class Test extends JFrame {
  9.    public static void main(String args[]) {
  10.       SwingApp.launch(new Test(), "A Sort Decorator"
  11.                                    300, 300, 200, 250);
  12.    }
  13.    public Test() {
  14.       // Create the decorator that will decorate the table's 
  15.       // original model. The reference must be final because it's
  16.       // accessed by an inner class below. 
  17.       final TableSortDecorator decorator = 
  18. new TableSortDecorator(table.getModel());
  19.       // Set the table's model to the decorator. Because the
  20.       // decorator implements TableModel, the table will never
  21.       // know the difference between the decorator and it's
  22.       // original model.
  23.       table.setModel(decorator);
  24.       ...
  25.       // Obtain a reference to the table's header
  26.       JTableHeader header = (JTableHeader)table.getTableHeader();
  27.       // React to mouse clicks in the table header by calling 
  28.       // the decorator's sort method.
  29.       header.addMouseListener(new MouseAdapter() {
  30.          public void mouseClicked(MouseEvent e) {
  31.             TableColumnModel tcm = table.getColumnModel();
  32.             int vc = tcm.getColumnIndexAtX(e.getX());
  33.             int mc = table.convertColumnIndexToModel(vc);
  34.             // Perform the sort
  35.             decorator.sort(mc);
  36.             // Update the status area
  37.             SwingApp.showStatus(headers[mc] + " sorted");
  38.          }
  39.       });
  40.    }
  41.    ...
  42. }


In the preceding code, a sort decorator decorates the table's original model. When you click on a column header, the application calls the decorator's sort() method. The interesting code in this example is not the application, but the sort decorator. Before looking at the source to TableSortDecorator, let's look at its class diagram, shown in Figure 7. 


[i]Figure 7. Sorting decorator class diagram[/i] 

Since instances of TableSortDecorator decorate table models, TableSortDecorator implements the TableModel interface. TableSortDecorator also maintains a reference to the real model that it decorates. That reference can point to any table model. Example 5 lists the TableSortDecorator class: 

Example 5. A table sort decorator 

  1. class TableSortDecorator implements TableModel,TableModelListener{
  2.    public TableSortDecorator(TableModel model) {
  3.       this.realModel = model;   
  4.       realModel.addTableModelListener(this);
  5.       allocate();
  6.    }
  7.    // The following nine methods are defined by the TableModel
  8.    // interface; all of those methods forward to the real model.
  9.    public void addTableModelListener(TableModelListener l) {
  10.       realModel.addTableModelListener(l);
  11.    }
  12.    public Class getColumnClass(int columnIndex) {
  13.       return realModel.getColumnClass(columnIndex);
  14.    }
  15.    public int getColumnCount() {
  16.       return realModel.getColumnCount();   
  17.    }
  18.    public String getColumnName(int columnIndex) {
  19.       return realModel.getColumnName(columnIndex);
  20.    }
  21.    public int getRowCount() {
  22.       return realModel.getRowCount();   
  23.    }
  24.    public boolean isCellEditable(int rowIndex, int columnIndex) {
  25.       return realModel.isCellEditable(rowIndex, columnIndex);
  26.    }
  27.    public void removeTableModelListener(TableModelListener l) {
  28.       realModel.removeTableModelListener(l);
  29.    }
  30.    public Object getValueAt(int row, int column) {
  31.       return getRealModel().getValueAt(indexes[row], column);
  32.    }
  33.    public void setValueAt(Object aValue, int row, int column) {
  34.       getRealModel().setValueAt(aValue, indexes[row], column);
  35.    }
  36.    // The getRealModel method is used by subclasses to
  37.    // access the real model.
  38.    protected TableModel getRealModel() {
  39.       return realModel;
  40.    }
  41.    // tableChanged is defined in TableModelListener, which
  42.    // is implemented by TableSortDecorator.
  43.    public void tableChanged(TableModelEvent e) {
  44.       allocate();   
  45.    }
  46.    // The following methods perform the bubble sort ...
  47.    public void sort(int column) {
  48.       int rowCount = getRowCount();
  49.       for(int i=0; i < rowCount; i++) {
  50.          for(int j = i+1; j < rowCount; j++) {
  51.             if(compare(indexes[i], indexes[j], column) < 0) {
  52.                swap(i,j);
  53.             }
  54.          }
  55.       }
  56.    }
  57.    private void swap(int i, int j) {
  58.       int tmp = indexes[i];
  59.       indexes[i] = indexes[j];
  60.       indexes[j] = tmp;
  61.    }
  62.    private int compare(int i, int j, int column) {
  63.       TableModel realModel = getRealModel();
  64.       Object io = realModel.getValueAt(i,column);
  65.       Object jo = realModel.getValueAt(j,column);
  66.       int c = jo.toString().compareTo(io.toString());
  67.       return (c < 0) ? -1 : ((c > 0) ? 1 : 0);
  68.    }
  69.    private void allocate() {
  70.       indexes = new int[getRowCount()];
  71.       for(int i=0; i < indexes.length; ++i) {
  72.          indexes[i] = i;         
  73.       }
  74.    }
  75.    private TableModel realModel; // We're decorating this model
  76.    private int indexes[];
  77. }


Table sort decorators do not change their real model when they sort; instead, decorators maintain an array of integers representing sorted row positions. When a sort decorator, masquerading as a table model, is asked for a value at a specific row and column, it uses the row value as an index into its array, and returns the corresponding value from the array. In that way, sort decorators overlay sorting onto Swing table models without modifying the original model in any way. 

TableSortDecorator also implements the TableModelListener interface and registers itself as a listener with the real model. When the real model fires a table changed event, the decorator reallocates its array of sorted index references. That allocation happens in TableSortDecorator.tableChanged. 

TableSortDecorator has 11 public methods. Nine of those methods forward to the real model. Of those nine methods, only two methods -- getValueAt() and setValueAt() -- provide any additional functionality. Such ratios are not unusual among decorators; most decorators add a small amount of behavior to a decorated object that already performs several functions. 

Refactoring the sort decorator

The sort decorator discussed above works as advertised by decorating arbitrary table models with sorting functionality at runtime. This gives you flexibility because the decorated models do not need to be modified, which means that any table model can be decorated with sorting capabilities. 

But the TableSortDecorator as implemented above is not designed for reuse because it implements two responsibilities for which it is not well suited. The first responsibility is forwarding to the real model. Because other table model decorators will need almost exactly the same code, that responsibility is too general, and should be moved up the class hierarchy. The second responsibility is sorting, which in this example is a bubble sort. That sorting algorithm is hardcoded into the sort decorator, which means sorting cannot be changed without rewriting the entire decorator. The sorting algorithm is too specific, so it should be moved down the hierarchy. 

Figure 8 shows the result of refactoring the sort decorator as recommended in the preceding paragraph. 


[i]Figure 8. Refactored sorting decorator class diagram. Click on thumbnail to view full-size image[/i]

The refactoring split the original TableSortDecorator into three different classes: 
  • TableModelDecorator: Implements TableModel by forwarding to the real model 

  • TableSortDecorator: Extends TableModelDecorator and adds an abstract sort method that subclasses must implement 

  • TableBubbleSortDecorator: Extends TableSortDecorator and implements the bubble sort 

By refactoring the sort decorator, we can reuse the code that forwards to the real table model. Encapsulating that code in TableModelDecorator lets us easily develop other table model decorator types, such as the filter decorators -- which are not discussed in this article -- shown in Figure 8. 

The abstract TableSortDecorator class defers the implementation of its sort() method to subclasses, making it trivial to implement sort decorators with a different sorting algorithm. 

Example 6 lists the TableModelDecorator class. 

Example 6. TableModelDecorator 

  1. import javax.swing.table.TableModel;
  2. import javax.swing.event.TableModelListener;
  3. // TableModelDecorator implements TableModelListener. That
  4. // listener interface defines one method: tableChanged(), which
  5. // is called when the table model is changed. That method is
  6. // not implemented in this abstract class; it's left for
  7. // subclasses to implement.
  8. public abstract class TableModelDecorator 
  9.                         implements TableModelTableModelListener {
  10.    public TableModelDecorator(TableModel model) {
  11.       this.realModel = model;   
  12.       realModel.addTableModelListener(this);
  13.    }
  14.    // The following 9 methods are defined by the TableModel
  15.    // interface; all of those methods forward to the real model.
  16.    public void addTableModelListener(TableModelListener l) {
  17.       realModel.addTableModelListener(l);
  18.    }
  19.    public Class getColumnClass(int columnIndex) {
  20.       return realModel.getColumnClass(columnIndex);
  21.    }
  22.    public int getColumnCount() {
  23.       return realModel.getColumnCount();   
  24.    }
  25.    public String getColumnName(int columnIndex) {
  26.       return realModel.getColumnName(columnIndex);
  27.    }
  28.    public int getRowCount() {
  29.       return realModel.getRowCount();   
  30.    }
  31.    public Object getValueAt(int rowIndex, int columnIndex) {
  32.       return realModel.getValueAt(rowIndex, columnIndex);
  33.    }
  34.    public boolean isCellEditable(int rowIndex, int columnIndex) {
  35.       return realModel.isCellEditable(rowIndex, columnIndex);
  36.    }
  37.    public void removeTableModelListener(TableModelListener l) {
  38.       realModel.removeTableModelListener(l);
  39.    }
  40.    public void setValueAt(Object aValue, 
  41.                         int rowIndex, int columnIndex) {
  42.       realModel.setValueAt(aValue, rowIndex, columnIndex);
  43.    }
  44.    // The getRealModel method is used by subclasses to
  45.    // access the real model.
  46.    protected TableModel getRealModel() {
  47.       return realModel;
  48.    }
  49.    private TableModel realModel; // We're decorating this model
  50. }


Notice that public TableModelDecorator methods do nothing but forward to the real model. That makes them good defaults for other classes to inherit when they extend TableModelDecorator. Example 7 lists TableSortDecorator. 

Example 7. TableSortDecorator 

  1. import javax.swing.table.TableModel;
  2. public abstract class TableSortDecorator extends 
  3.                                          TableModelDecorator {
  4.    // Extensions of TableSortDecorator must implement the
  5.    // abstract sort method, in addition to tableChanged. The
  6.    // latter is required because TableModelDecorator
  7.    // implements the TableModelListener interface.
  8.    abstract public void sort(int column);
  9.    public TableSortDecorator(TableModel realModel) {
  10.       super(realModel);
  11.    }
  12. }


The abstract TableSortDecorator class defines one abstract method -- sort() -- that must be implemented by concrete subclasses. The TableBubbleSortDecorator, listed in Example 8, is one such class. 

Example 8. TableBubbleSortDecorator 

  1. import javax.swing.table.TableModel;
  2. import javax.swing.event.TableModelEvent;
  3. public class TableBubbleSortDecorator extends TableSortDecorator {
  4.    // The lone constructor must be passed a reference to a
  5.    // TableModel. This class decorates that model with additional
  6.    // sorting functionality.
  7.    public TableBubbleSortDecorator(TableModel model) {
  8.       super(model);
  9.       allocate();
  10.    }
  11.    // tableChanged is defined in TableModelListener, which
  12.    // is implemented by TableSortDecorator.
  13.    public void tableChanged(TableModelEvent e) {
  14.       allocate();   
  15.    }
  16.    // Two TableModel methods are overridden from
  17.    // TableModelDecorator ...
  18.    public Object getValueAt(int row, int column) {
  19.       return getRealModel().getValueAt(indexes[row], column);
  20.    }
  21.    public void setValueAt(Object aValue, int row, int column) {
  22.       getRealModel().setValueAt(aValue, indexes[row], column);
  23.    }
  24.    // The following methods perform the bubble sort ...
  25.    public void sort(int column) {
  26.       int rowCount = getRowCount();
  27.       for(int i=0; i < rowCount; i++) {
  28.          for(int j = i+1; j < rowCount; j++) {
  29.             if(compare(indexes[i], indexes[j], column) < 0) {
  30.                swap(i,j);
  31.             }
  32.          }
  33.       }
  34.    }
  35.    private void swap(int i, int j) {
  36.       int tmp = indexes[i];
  37.       indexes[i] = indexes[j];
  38.       indexes[j] = tmp;
  39.    }
  40.    private int compare(int i, int j, int column) {
  41.       TableModel realModel = getRealModel();
  42.       Object io = realModel.getValueAt(i,column);
  43.       Object jo = realModel.getValueAt(j,column);
  44.       int c = jo.toString().compareTo(io.toString());
  45.       return (c < 0) ? -1 : ((c > 0) ? 1 : 0);
  46.    }
  47.    private void allocate() {
  48.       indexes = new int[getRowCount()];
  49.       for(int i=0; i < indexes.length; ++i) {
  50.          indexes[i] = i;         
  51.       }
  52.    }
  53.    private int indexes[];
  54. }


With the refactoring of the original table sort decorator, we now have a hierarchy of table decorators that we can extend at will. 

Decorator applicability

In Java, developers typically use inheritance to add functionality to objects. Base classes implement shared behavior, and extensions add functionality. For example, instead of the sort and filter decorators discussed above, you could implement SortModel, FilterModel, and, perhaps, SortFilterModel classes. 

Decorators prove more flexible than inheritance because the relationship between decorators and the objects they decorate can change at runtime, but relationships between base classes and their extensions are fixed at compile time. 

Of course, both inheritance and the Decorator pattern have their places; in fact, inheritance is used to implement the Decorator pattern. So when should you use the Decorator pattern? It's useful when: 
  • You have many simple behaviors you would like to combine in numerous ways at runtime. Implementing the functionality with inheritance would result in a subclass explosion for every possible combination of behaviors. 

  • You need to transparently add functionality to individual objects at runtime. Transparently means that existing classes need not have been modified to support the extra functionality; indeed, those classes are not aware that they are being decorated. 

  • You want to restrict the use of an object's public methods. Instead of forwarding calls to a restricted method, a decorator can veto a method by throwing an exception from the wrapper method. 


  --相关文章--
· 面向对象编程,我的思想 (2007-04-13)
· 面向对象的思维方式 (2007-04-13)
· 通过Javanbsp;Swing看透MVC设计模式 (2007-04-13)
· 适配器模式(Adapternbsp;Pattern) (2007-04-13)
· 追MM与Java的23种设计模式 (2007-04-13)
· 责任链模式(Chainnbsp;ofnbsp;Responsibility) (2007-04-13)

版权所有©2005-2006 JAVA资源网 渝ICP备05007591号 虚拟主机 | 关于我们 | 联系方式 | 广告业务 | 网站地图 | 友情链接