2008年9月8日星期一

A Swing Architecture Overview(二)

模型可分离架构(Separable model architecture)
一般认为以应用的数据为架构的中心比以用户接口为中心的做法要更好,为了支持这种范例, swing为每个拥有数据逻辑或数据抽象的组件都定义了一个单独的模型接口。这种分离使得程序可以在swing组件里面选择拔插它们自己实现的模型。

下面的表格显示了swing里面的组件和模型之间的对应关系:

Component Model Interface Model Type
JButton ButtonModel GUI
JToggleButton ButtonModel GUI/data
JCheckBox ButtonModel GUI/data
JRadioButton ButtonModel GUI/data
JMenu ButtonModel GUI
JMenuItem ButtonModel GUI
JCheckBoxMenuItem ButtonModel GUI/data
JRadioButtonMenuItem ButtonModel GUI/data
JComboBox ComboBoxModel data
JProgressBar BoundedRangeModel GUI/data
JScrollBar BoundedRangeModel GUI/data
JSlider BoundedRangeModel GUI/data
JTabbedPane SingleSelectionModel GUI
JList ListModel data
JList ListSelectionModel GUI
JTable TableModel data
JTable TableColumnModel GUI
JTree TreeModel data
JTree TreeSelectionModel GUI
JEditorPane Document data
JTextPane Document data
JTextArea Document data
JTextField Document data
JPasswordField Document data

GUI状态模型与应用数据模型(GUI-state vs.application-data models)
Swing的模型大致分为两种:GUI状态模型和应用数据模型。

GUI状态模型(GUI-state models)
GUI状态模型是定义一个GUI控制器的可视状态的接口,比如button是否按下或松开,list里面的items是否被选上。GUI状态模型常常是与图形用户接口(GUI)的上下文有联系的。使用GUI状态模型分离对开发程序常常非常有帮助,特别是多个GUI控制器与一个公用的状态(比如在一个共享白板程序)有联系的时候,或者如果操纵一个控制器自动改变其它控制器的值时候,swing不需要使用GUI状态模型。通过组件的最顶层的方法可以操纵GUI控制器的状态,完全不需要直接与模型进行交互。在前文描述的表格中,swing的GUI状态模型用蓝色显示。

应用数据模型(Application-data models)
应用数据模型是指表示应用上下文中的一些具有重要意义的可以计量的数据的接口,像一个table里面的一个cell的值或在list里面显示的items的值等。这些数据模型为需要在GUI 与应用数据/逻辑之间有彻底的分离的swing程序提供了非常强大的编程范例。对于像JTreeJTable这样真正以数据为中心的swing组件,强烈推荐与数据模型进行交互。应用数据模型在本章节开始部分显示的表格里面使用红色进行了高亮显示。

当然,有一些组件在GUI状态模型和应用数据模型之间的分类取决于该模型在上下文中的使用。JProgressBar或者JSlider里面的BoundedRangeModel就是这种情况。这些模型在前面的table中用紫色做了高亮显示。

Swing的可分离模型API GUI状态模型和应用数据模型之间没有明显的区别,我们在这里阐明这些不同之处的目的是帮助开发人员更好的理解为什么和什么时候他们可能会希望使用到可分离的模型编程。

共享模型定义(Shared model definitions)
再次谈到本章开始的时候显示的table,注意在组件之间共享的模型定义在每个组件之间的数据抽象是如此相似,以至于可以支持同一个接口。公共模型具有能自动配合多个组件类型的能力。举例来说,JSliderJScrollbar都使用BoundedRangeModel接口,一个BoundedRangeModel实例同时可以被一个JScrollbar和一个JSlider 使用,它们的可视状态可以一直都保持同步。

可分离模型API(The separable-model API

Swing组件定义的模型支持JavaBeans值绑定模型。比如,JSlider 使用了BoundedRangeModel接口作它的模型定义,因此,它包含下面的方法:
public BoundedRangeModel getModel()
public void setModel(BoundedRangeModel model)
所有的swing组件都有一样共同的东西: 如果你不设置你自己的模型,在组件内部将会自动创建和安装一个缺省的模型。这些模型类的名称约定为在接口名称的最前面加一个“Default”。如JSlider,在它的构造函数里面就有一个DefaultBoundedRangeModel对象的示例。
public JSlider(int orientation, int min,int max, int value)
{
checkOrientation(orientation);
this.orientation = orientation;
this.model
=new DefaultBoundedRangeModel(value, 0, min, max);
this.model.addChangeListener(changeListener);
updateUI();
}
如果程序后来调用了setModel()方法,这个缺省的模型将会被替换,如例所示:
JSlider slider = new JSlider();
BoundedRangeModel myModel =
new DefaultBoundedRangeModel() {
public void setValue(int n) {
System.out.println("SetValue: "+ n);
super.setValue(n);
}
});
slider.setModel(myModel);
对于更复杂的模型(比如JTable和JList等的模型),为开发人员提供了一个抽象的模型实现,这样他们不至于一切都从头开始。这些类的前缀为“Abstract”。

比如,JList的模型接口为ListModel,该模型接口同时提供了DefaultListModel和AbstractListModel类来帮助开发人员组建一个list模型。

模型改变通知(Model change notification)
当模型的数据或者值改变之后必须能够通知所有相关的部分(如视图)。Swing模型使用JavaBeans Event模型来实现这个通知。在swing中使用了两个方案来实现这个通知。

当状态发生改变的时候发送一个轻量级通知,这需要事件监听器通过查询模型发生了什么变化来作出响应。这个方案的优点是一个事件实例可以同时被一个模型的所有通知来使用,这在通知被高频率使用的时候特别需要(比如当一个JScrollBar被拖动的时候)。

发送一个详细描述模型发生了什么改变的有状态的通知。这个方案需要每个通知都有一个新的事件实例。使用这个方案的场合是在一般的通知无法提供足够的信息给事件监听器通过查询模型来有效判断什么发生了变化(比如当一个JTable的一列单元的值发生了变化)。

轻量级通知(Lightweight notification)
下面的swing模型使用了轻量级通知,它们基于ChangeListener/ChangeEvent API:


Model Listener Event
BoundedRangeModel ChangeListener ChangeEvent
ButtonModel ChangeListener ChangeEvent
SingleSelectionModel ChangeListener ChangeEvent

这个ChangeListener接口有只有一个很普通的方法:
public void stateChanged(ChangeEvent e)
在一个ChangeEvent中只有一个唯一的状态即事件的“源”,因为在很多通知中都使用同一个源,在某些特定的模型里一个event实例可以被所有的通知使用。模型采用该机制来支持下面的添加和移除ChangeListeners的方法。
public void addChangeListener(ChangeListener l)
public void removeChangeListener(ChangeListener l)
所以,当一个JSlider的值发生改变的时候发出通知的代码可能如下:
JSlider slider = new JSlider();
BoundedRangeModel model = slider.getModel();
model.addChangeListener(new ChangeListener() {
public void stateChanged(ChangeEvent e) {
// need to query the model
// to get updated value...
BoundedRangeModel m =(BoundedRangeModel)e.getSource();
System.out.println("model changed: " +m.getValue());
}
});
有人不希望和可分离模型对象打交道,为了提供方便,有些swing类还提供了直接在组件上注册ChangeListeners的能力(这样组件能够在内部监听到模型的变化,然后将那些事件传播给任何直接注册在组件上的监听器),这些通知的唯一不同之处在于对于模型来说,事件源是模型的实例,而对组件来说,事件源是组件。

我们可以简单的举个例子:
JSlider slider = new JSlider();
slider.addChangeListener(new ChangeListener() {
public void stateChanged(ChangeEvent e) {
// the source will be
// the slider this time..
JSlider s = (JSlider)e.getSource();
System.out.println("value changed: " +
s.getValue());
}
});

有状态的通知(Stateful notification)
模型根据事件监听器接口和事件对象的用途提供有状态的通知,下面的表格显示了这些模型的详细分类:

Model Listener Event
ListModel ListDataListener ListDataEvent
ListSelectionModel ListSelectionListener ListSelectionEvent
ComboBoxModel ListDataListener ListDataEvent
TreeModel TreeModelListener TreeModelEvent
TreeSelectionModel TreeSelectionListener TreeSelectionEvent
TableModel TableModelListener TableModelEvent
TableColumnModel TableColumnModel-
Listener
TableColumnModel-
Event
Document DocumentListener DocumentEvent
Document UndoableEditListener UndoableEditEvent

这些APIs的使用方法与轻量级通知相似,除了那些可以直接查询事件对象来发现什么发生的变化的事件监听器之外。举例来说,下面的代码动态的跟踪了JList中被选上的item。

String items[] = {"One", "Two", "Three");
JList list = new JList(items);
ListSelectionModel sModel = list.getSelectionModel();
sModel.addListSelectionListener(new ListSelectionListener() {
public void valueChanged(ListSelectionEvent e) {
// get change information directly
// from the event instance...
if (!e.getValueIsAdjusting()) {
System.out.println("selection changed: " +e.getFirstIndex());
}
}
});
自动更新视图(Automatic View Updates)
模型与表现它的视图没有任何内在联系。(具有相关性的做法在多个视图使用同一个模型的时候会受到指责)。模型只有一些对它的状态什么发生变化感兴趣的事件监听器。一个swing 组件负责钩住与其相关的模型监听器,这样当模型发生变化的时候它可以准确的重绘它本身(如果你发现当模型发生变化时一个组件没有自动更新,这会是一个bug!) 。不管是使用缺省的内部模型还是使用程序自己实现的模型都是这样的。

完全忽视模型(Ignoring models completely)
前文提到,大多组件提供了在组件类中直接进行定义模型API,所以可以不通过与模型交互就可以对组件进行操作。这是非常令人满意的编程范例(特别是GUI状态模型)。比如,下面是JSlider的getValue() ,方法实现,它会在内部代理调用它的模型的方法。
publi c int getValue() {
return getModel().getValue();
}
于是程序就变的如下这么简单了:
JSlider slider = new JSlider();
int value = slider.getValue();
//what's a "model," anyway?
可分离模型总结(Separable model summary)
理解swing的模型设计是如何工作的是非常有用的,在swing编程的各个场合都使用模型API是不必要的。你应该仔细考虑你的应用的具体需要,然后决定在什么地方使用模型API,不用搞一些不必要的复杂性就可以改善你的代码。

在特殊情况下,我们推荐在swing中使用应用数据类的模型(比如JTable和JTree等的模型),因为从长远来看它们可以很大的提高你的应用的模块性和可测量性。


来源:
http://www.blogjava.net/azure/archive/2007/05/05/115441.html
http://java.sun.com/products/jfc/tsc/articles/architecture/ui_install/index.html

没有评论:

我的简介