我想实现一个 TableModel,这将使我现有的数据结构能够通过 JTable 显示。
假设我的数据结构是一个简单的 List
,其中 MyRow 是:
public class MyRow {
String firstName;
String secondName;
}
我的想法是这个类在显示时“应该尽自己的职责”,所以我添加了以下方法,它只是将每个字段映射到一个索引:
public Object getValueAt(int columnIndex) {
switch (columnIndex) {
case 0: return firstName;
case 1: return secondName;
default: return null;
}
}
此时,TableModel
实现将很简单:
public class MyModel extends AbstractTableModel {
ArrayList<MyRow> list = new ArrayList<MyRow>();
public int getRowCount() {return list.size();}
public int getColumnCount() {return 2;}
public Object getValueAt(int rowIndex, int columnIndex) {
return list.get(rowIndex).getValueAt(columnIndex);
}
}
很整洁,不是吗? :-)
好的,但是 return 2;
让我很烦恼。在这个“体系结构”中,我已将表示行的可解释性分配给 MyRow,因此应该由 MyRow 给出列数。这是因为它提供了与列数相关的 getValueAt(int columnIndex) 方法:columnsIndex
即使 List
中没有行,因此也没有 MyRow
实例,也可能会询问这条信息。因此,添加到 MyRow 的方法应该是静态方法:
public class MyRow {
...
public static int getColumnCount() { return 2; }
...
并且 MyModel 的 getColumnCount() 方法将进行相应的修改:
public int getColumnCount() {
//return 2; //No more this *Encapsulation Violation*
return MyRow.getColumnCount();
}
现在 MyRow 包含表示一行所需的所有数据。很容易证明:添加、删除或更改列只是修改 MyRow 的问题。
干净,不是吗? :-) :-)
问题是,使用静态方法,我无法为 MyRow 提供抽象超类。
这应该是显而易见的,但可以肯定的是:
public abstract class MyAbstractRow {
public static int getColumnCount() { return 0; }
}
然后
public class MyRow extends MyAbstractRow{
String firstName;
String secondName;
public static int getColumnCount() { return 2; }
}
我实现一个“通用”MyData,它使用 MyAbstractRow:
public int getColumnCount() {
//return 2; //No more this *Encapsulation Violation*
return MyAbstractRow.getColumnCount();
}
这是在静态类型上解析的(因此返回 0),因为 MyData 的 getColumnCount 甚至没有对我实际上想用作 MyAbstractRow 的具体实现的类型。
如何解决这个问题,同时保持迄今为止所达到的良好封装水平?
I want to implement a TableModel, that will enable my existing data structure to be displayed by a JTable.
Say my data structure is a simple List
<MyRow>, where MyRow is:
public class MyRow {
String firstName;
String secondName;
}
My idea is that this class "should do its part" in being displayed, so i add it the following method, that simply maps each field to an index:
public Object getValueAt(int columnIndex) {
switch (columnIndex) {
case 0: return firstName;
case 1: return secondName;
default: return null;
}
}
At this point, the TableModel
implementation will simply be:
public class MyModel extends AbstractTableModel {
ArrayList<MyRow> list = new ArrayList<MyRow>();
public int getRowCount() {return list.size();}
public int getColumnCount() {return 2;}
public Object getValueAt(int rowIndex, int columnIndex) {
return list.get(rowIndex).getValueAt(columnIndex);
}
}
Neat, isn't it? :-)
Ok, but return 2;
annoys me. In this "architecture" i have assigned to MyRow the rexposibility to represent a row, and so it should be MyRow that gives the number of columns. That because it provides the getValueAt(int columnIndex)
method that is related to the number of columns: columnsIndex<columnCount.
This piece of information may be asked even if there are no rows in the List
, so no instances of MyRow
. So the method to add to MyRow should be a static method:
public class MyRow {
...
public static int getColumnCount() { return 2; }
...
And the getColumnCount() method of MyModel will be simply modified accordingly:
public int getColumnCount() {
//return 2; //No more this *Encapsulation Violation*
return MyRow.getColumnCount();
}
Now MyRow contains all the data needed to represent a row. It is easy to show that: adding, removing or changing columns is just question of modifying MyRow.
Clean, isn't it? :-) :-)
The problem is that with static methods, i can't provide an abstract super-class for MyRow.
This should be obvious, but to be sure:
public abstract class MyAbstractRow {
public static int getColumnCount() { return 0; }
}
then
public class MyRow extends MyAbstractRow{
String firstName;
String secondName;
public static int getColumnCount() { return 2; }
}
then i implement a "generic" MyData, that uses a MyAbstractRow:
public int getColumnCount() {
//return 2; //No more this *Encapsulation Violation*
return MyAbstractRow.getColumnCount();
}
This is resolved on the static type (so returns 0) because the getColumnCount of MyData doesn't even have a reference to the type i actually want to use as my concrete implementation of MyAbstractRow.
How to solve this problem, mantaining the good level of encapsulation achieved so far?
发布评论
评论(1)
您正在混合模型和视图概念。您的模型 MyRow 不应该知道它将如何表示,因此它不应该知道有多少列、它们包含什么等等...这就是您遇到此问题的原因(使用静态方法来知道列数)。
更干净地执行此操作的一种方法是让 TableModel 返回列数,并通过调用 MyRow 类的成员来实现 getValueAt。您是否查看了默认实现(DefaultTableModel)。
现在我不明白为什么你需要重写静态方法(这是不可能的)。如果您需要为相互继承的不同对象拥有不同的表模型,那么您可以考虑以相同的方式创建相互继承的不同表模型。
You are mixing model and view concepts. Your model MyRow should not know how it will be represented and so it should not know how many columns there are, what they contain, etc... This is why you run into this problem (using a static method to know the number of columns).
One way to do this more cleanly is to have your TableModel return the number of columns and also implement the getValueAt by invoking the members of your MyRow class. Did you take a look at the default implementation (DefaultTableModel).
Now I don't understand why you need to override static methods (which is impossible). If you need to have different table models for different objects that inherit each other, then you might consider creating different table models that inherit each other in the same way.