通过Java Swing看破MVC设计模式
副标题#e#
一个好的用户界面(GUI)的设计凡是可以在现实世界找到相应的表示。譬喻,假如在您的眼前摆放着一个雷同于电脑键盘按键的一个简朴的按钮,然而就是这么简朴的一个按钮,我们就可以看出一个GUI设计的法则,它由两个主要的部门组成,一部门使得它具有了按钮应该具有的行动特性,譬喻可以被按下。别的一部门则认真它的表示,譬喻这个按钮是代表了A照旧B。
看清楚这两点你就发明白一个很强大的设计要领,这种要领勉励重用reuse,而不是从头设计redesign。你发明按钮都有沟通的机理,你只要在按钮的顶上喷上差异的字母便能制造出“差异”的按钮,而不消为了每个按钮而从头设计一份图纸。这大大减轻了设计事情的时间和难度。
假如您把上述设计思想应用到软件开拓规模,那么取得相似的结果一点都不让人诧异。一个在软件开拓规模应用的很是遍及的技能Model/View/Controller(MVC)即是这种思想的一个实现。
这虽然很不错,可是或者您又开始迷惑这和java基本类JFC(Java Foundation Class)中的用户界面设计部门(Swing)又有什么干系呢?好的,我来汇报你。
尽量MVC设计模式凡是是用来设计整个用户界面(GUI)的,JFC的设计者们却独创性的把这种设计模式用来设计Swing中的单个的组件(Component),譬喻表格Jtable,树Jtree,组合下拉列表框JcomboBox等等等等。这些组件都有一个Model,一个View,一个Controller,并且,这些model,view,controller可以独立的改变,就是当组件正在被利用的时候也是如此。这种特性使得开拓GUI界面的东西包显得很是的机动。
好,来吧,让我来汇报你它是如何事情的。
MVC设计模式
就象我适才指出的一样,MVC设计模式把一个软件组件区分为三个差异的部门,model,view,controller。
Model是代表组件状态和初级行为的部门,它打点着本身的状态而且处理惩罚所有对状态的操纵,model本身自己并不知道利用本身的view和controller是谁,系统维护着它和view之间的干系,当model产生了改变系统还认真通知相应的view。
View代表了打点model所含有的数据的一个视觉上的泛起。一个Model可以有一个以上的View,可是Swing中却很少有这样的环境。
Controller打点着model和用户之间的交互的节制。它提供了一些要领去处理惩罚当model的状态产生了变革时的环境。
利用键盘上的按钮的例子来说明一下:Model就是按钮的整个机器装置,View/Controller就是按钮的外貌部门。
下面的图表明白如何把一个JFC开拓的用户界面分为model,view,controller,留意,view/Controller被归并到了一起,这是MVC设计模式凡是的用法,它们提供了组件的用户界面(UI)。
#p#副标题#e#
用Button的例子具体说明
为了更好的领略MVC设计模式和Swing用户界面组件之间的干系,让我们越发深入的举办阐明。我将回收最常见的组件button来说明。
我们从model来开始。
Model
一个按钮的model所应该具备的行为由一个接口ButtonModel来完成。一个按钮model实例封装了其内部的状态,而且界说了按钮的行为。它的所有要领可以分为四类:
1、查询内部状态
2、操纵内部状态
3、添加和删除事件监听器
4、产闹事件
其他的用户界面组件有它们各自的与组件相关的Model,可是所有的组件Model都提供这四类要领。
View & Controller
上面的图中报告一个按钮的view/controller由一个接口ButtonUI完成。假如一个类实现了这个接口,那么它将会认真建设一个用户界面,处理惩罚用户的操纵。它的所有要领可以被分为三大类:
1、绘制Paint
2、返回几许范例的信息
3、处理惩罚AWT事件
其他用户界面组件有他们本身的组件相关的View/Controller,可是他们都提供上述三类要领。
措施员凡是并不会直接和model以及view/controller打交道,他们凡是埋没于那些担任自java.awt.Component的组件内里了,这些组件就像胶水一样把MVC三者合三为一。也正是由于这些担任的组件工具,一个措施员可以很利便的殽杂利用Swing组件和AWT组件,然后,我们知道,Swing组件有许多都是直接担任自相应的AWT组件,它能提供比AWT组件越发利便易用的成果,所以凡是环境下,我们没有须要殽杂利用两者。
一个实例
此刻我们已经大白了Java类与MVC各个部门的对应干系,我们可以越发深入一点去阐明问题了。下面我们将要报告一个小型的利用MVC模式开拓的例子。因为JFC十分的巨大,我只能把我的例子范围于一个用户界面组件内里(假如你猜是一个按钮的例子,那么你对了!)
让我们来看看这个例子的所有部门吧。
Button类
最显而易见的开始的处所就是代表了按钮组件本省的代码,因为这个类是大部门措施员会打仗的。
#p#分页标题#e#
就像我前面提到的,按钮用户界面组件类实际上就是model和view/controller的之间的黏合剂。每个按钮组件都和一个model以及一个controller关联,model界说了按钮的行为,而view/controller界说了按钮的表示。而应用措施可以在任何事件改变这些关联。让我们看看得以实现此成果的代码。
public void setModel(ButtonModel buttonmodel)
{
if (this.buttonmodel != null)
{
this.buttonmodel.removeChangeListener(buttonchangelistener);
this.buttonmodel.removeActionListener(buttonactionlistener);
buttonchangelistener = null;
buttonactionlistener = null;
}
this.buttonmodel = buttonmodel;
if (this.buttonmodel != null)
{
buttonchangelistener = new ButtonChangeListener();
buttonactionlistener = new ButtonActionListener();
this.buttonmodel.addChangeListener(buttonchangelistener);
this.buttonmodel.addActionListener(buttonactionlistener);
}
updateButton();
}
public void setUI(ButtonUI buttonui)
{
if (this.buttonui != null)
{
this.buttonui.uninstallUI(this);
}
this.buttonui = buttonui;
if (this.buttonui != null)
{
this.buttonui.installUI(this);
}
updateButton();
}
public void updateButton()
{
invalidate();
}
在进入下一节之前,你应该多花一些时间来仔细阅读一下Button类的源代码。
ButtonModel类
ButtonModel维护着三种范例的状态信息:是否被按下(pressed),是否“武装上了”(armed),是否被选择(selected)。它们都是boolean范例的值。
一个按钮被按下(pressed)是指当鼠标在按钮上面的时候,按下鼠标可是还没有松开鼠标按钮的状态,实时用户此时把鼠标拖拽到按钮的外面也没有改变这种状态。
一个按钮是否“武装了”(armed)是指按钮被按下,而且鼠标还在按钮的上面。
一些按钮还大概被选择(selected),这种状态通过反复的点击按钮取得true可能false的值。
下面的代码是状态pressed的一个缺省的实现。状态armed以及selected实现的代码与之雷同。ButtonModel类应该被担任,这样可以包围缺省的状态界说,实现有本性的按钮。
private boolean boolPressed = false;
public boolean isPressed()
{
return boolPressed;
}
public void setPressed(boolean boolPressed)
{
this.boolPressed = boolPressed;
fireChangeEvent(new ChangeEvent(button));
}
按钮的模子button model还认真通知其他工具(事件监听器)它们所感乐趣的事件。从下面的代买中我们可以看出当按钮的转台产生改变的时候就会发出一个ChangeEvent。下面就是代码:
private Vector vectorChangeListeners = new Vector();
public void addChangeListener(ChangeListener changelistener)
{
vectorChangeListeners.addElement(changelistener);
}
public void removeChangeListener(ChangeListener changelistener)
{
vectorChangeListeners.removeElement(changelistener);
}
protected void fireChangeEvent(ChangeEvent changeevent)
{
Enumeration enumeration = vectorChangeListeners.elements();
while (enumeration.hasMoreElements())
{
ChangeListener changelistener =(ChangeListener)enumeration.nextElement();
changelistener.stateChanged(changeevent);
}
}
在进入下一节之前,你应该多花一些时间来仔细阅读一下ButtonModel类的源代码。
ButtonUI类
按钮的view/controller是认真构建暗示层的。缺省环境下它仅仅是用配景致画一个矩形罢了,他们的子类担任了他们而且包围了绘制的要领,使得按钮可以有很多差异的表示,譬喻MOTIF,Windows 95,Java样式等等。
public void update(Button button, Graphics graphics)
{
}
public void paint(Button button, Graphics graphics)
{
Dimension dimension = button.getSize();
Color color = button.getBackground();
graphics.setColor(color);
graphics.fillRect(0, 0, dimension.width, dimension.height);
}
ButtonUI类并不本身处理惩罚AWT事件,他们会利用一个定制的事件监听器把初级的AWT事件翻译为高级的Button模子期望的语义事件。下面就是安装/卸载事件监听器的代码。
#p#分页标题#e#
private static ButtonUIListener buttonuilistener = null;
public void installUI(Button button)
{
button.addMouseListener(buttonuilistener);
button.addMouseMotionListener(buttonuilistener);
button.addChangeListener(buttonuilistener);
}
public void uninstallUI(Button button)
{
button.removeMouseListener(buttonuilistener);
button.removeMouseMotionListener(buttonuilistener);
button.removeChangeListener(buttonuilistener);
}
View/Controller实际上就是一些要领。他们不维护任何本身的状态信息。因此,很多按钮的实例可以共享一个ButtonUI实例。ButtonUI是通过在方面的参数列表内里加上按钮的引用来区分各个差异的按钮。
同样,但愿你能多花一些时间来看看ButtonUI类,然后咱们进入下一节。
ButtonUIListener类
ButtonUIListener类可以辅佐Button类去转变鼠标可能键盘的输入为对按钮模子的操纵。这个监听器类实现了:MouseListener,MouseMotionListener,ChangeListener接口,而且处理惩罚一下事件:
public void mouseDragged(MouseEvent mouseevent)
{
Button button = (Button)mouseevent.getSource();
ButtonModel buttonmodel = button.getModel();
if (buttonmodel.isPressed())
{
if (button.getUI().contains(button, mouseevent.getPoint()))
{
buttonmodel.setArmed(true);
}
else
{
buttonmodel.setArmed(false);
}
}
}
public void mousePressed(MouseEvent mouseevent)
{
Button button = (Button)mouseevent.getSource();
ButtonModel buttonmodel = button.getModel();
buttonmodel.setPressed(true);
buttonmodel.setArmed(true);
}
public void mouseReleased(MouseEvent mouseevent)
{
Button button = (Button)mouseevent.getSource();
ButtonModel buttonmodel = button.getModel();
buttonmodel.setPressed(false);
buttonmodel.setArmed(false);
}
public void stateChanged(ChangeEvent changeevent)
{
Button button = (Button)changeevent.getSource();
button.repaint();
}
总结
我但愿你能凭据上面报告的要领去做。假如不能,那么所有的尽力都将白搭。这个例子以及Swing用户界面组件的长处在于你不消去花时间去弄大白他们底层是如何设计实现的就可以很利便的利用他们了。他们都提供了缺省的model以及view/controller,然后,当你本身做组件的时候,你会发明上面的思想的强大之处。