MVC在Java GUI项目中具体应用
论文摘要
n 在Java GUI项目中,程序设计或者编码时经常会碰到一些头疼的问题,例如需要的数据在自己需要的时候得不到,无奈增加一些静态变量或者公共函数来取得数据,或者应该刷新界面的时候得不到相关组件的对象等等。本来很好的设计最后被改的面目全非,甚至会被推翻了。本文提出自己总结的一个基于MVC模式的设计架构,来解决Java GUI项目中的一些常见问题。
关键字
Java GUI MVC
参考资料
n 《Java 设计模式》 JDK帮助文档
1. 系统结构图
1.1. 模型Model
数据模型部分系统结构图:
模型主要包含四部分。
一、组件数据模型的注册管理器。主要管理组件的数据模型的注册以及在数据变化时能把及时通知数据模型。而具体的数据模型会把数据模型中的数据转换成适合的格式。组件模型层管理器提供一系列的组件需要的数据模型。
二、数据结构的基础操作。数据结构的一些基础操作。基于这些基础操作,数据操作层可以做成更复杂的功能的操作。
三、提供给程序直接调用的操作。这些操作可以分为可以可以UNDO,不可以UNDO,对UNDO没影响的三种。
1.2. 控制Control
控制主要包括系统结构图中的组件层的事件处理和动作命令中中的动作生成。
一、组件层的事件处理:主要是对各个组件的鼠标事件,键盘事件,以及系统整理的快捷键的事件监听。例如按钮的点击事件,树组件中节点展开事件等等,这个都需要根据用户式样中的规定来对应处理,随需应变。
二、动作命令层。该层中的操作比较比较好定义。一般来说,跟系统中的菜单,工具按钮,快捷键是相互对应的,该层为了确保相同功能的菜单项,工具按钮和快捷键能同步处理,也即菜单项,工具按钮和快捷键的功能能调用同一段代码。该功能主要参考Swing中JMenuItem和JButton使用Action对象的机制来建立的。利用这一机制,可以让一个Action对象控制多个JMenuItem和JButton。虽然在Swing中只能在JMenuItem和JButton中使用该机制,但是我们自己可以建立其他组件的这种机制。甚至能够建立自己的类库。
1.3. 视图View
视图主要包括组件层。包括按钮,菜单,菜单项,文本框,文本区域,树,表等一系列组件,也可以把简单组件合成的复合组件,例如对话框等等。
2. 常见问题的解决
本章介绍以下功能的对应:
A.菜单条中菜单项,工具条中的按钮和快捷键可用性的统一管理。
B.UNDO/REDO功能
C.LOG功能和异常管理
D.操作互斥的处理
2.1. 菜单项,按钮和快捷键可用性的统一管理
XXX(内部项目)组中的成员可能都了解,在XXX(内部项目)中系统功能菜单项,按钮以及快捷键的可用性都是分别对应的,也就是说都是各自独立的,当需要设置某个功能无效时,只能分别去设置菜单项,按钮以及快捷键的可用性,相同的代码分散在三个地方,如果再加一个相同功能的右键菜单呢,冗余程度也就可想而知。
要实现该功能,我们可以操作Swing中Action类的功能。JMenuItem和JButton都有一个参数类型是Action的构造函数,当使用Action对象做参数建立JMenuItem和JButton对象时,可以设置JMenuItem和JButton的一些基本属性,如现实的文字,图标等等,还可以设置快捷键,最关键的一点是Action对象会与JMenuItem和JButton关联,把Action设置成不可用,那么它关联的所有JMenuItem和JButton都变成不可用的。在Action中设置的快捷键还会自动绑定到系统中去。
但在这个地方需要使用工厂模式,使用一个工厂类ActionManager来确保Action对象唯一。保存Action的最佳方法就是哈希表,哈希表的key值可以选择Action的Class对象或者Action的类路径。这样又可以增加另外一个好处,就是可以在配置文件中配置菜单条和工具条,其中就可以使用的Action的类路径来设置JMenuItem和JButton,在建立工具条时在结合ActionManager和Java中映射就可以了。
听来听去,你或许会以为实现这些功能的代码会很多,但是你错了,这里面有很多都是Swing给你做好的,很多都是现成的。
2.2. UNDO/REDO功能
这个功能主要参考Microsoft Word中的undo/redo的实现方法。使用的设计模式是命令模式。数据操作层中的一个操作就是一个命令。这些命令分为可以UNDO/REDO,不可以UNDO/REDO和不影响UNDO/REDO三种。每个操作都保存该操作进行时的参数和操作后的结果数据。
如果遇到可以UNDO/REDO的操作,则把该操作保存在UNDO/REDO操作栈中。
如果遇到不可以UNDO/REDO的操作,则清空UNDO/REDO操作栈中的所有操作。
如果遇到不影响UNDO/REDO的操作,保持UNDO/REDO操作栈不变。
当执行UNDO命令时,从UNDO/REDO操作栈中取出前一次的操作对象并调用undo方法,当执行REDO命令时,从UNDO/REDO操作栈中取出后一次的操作对象并调用redo方法。总之遵循一个原则,谁改变的事情谁就知道该如何还原。
2.3. LOG功能和异常管理
LOG的信息的建立和写入都由操作来完成,也即谁做的事情谁记录。输出LOG地方可以用相应的方法传入。一个具体的操作对象自己知道该操作是否成功,该产生什么日志,发生错误时该如何处理等等。明确责任到具体操作。
2.4. 操作互斥的处理
在GUI的项目中,多个操作往往可以同时执行,相互影响,继而产生负面作用。为解决这个问题,我们需要在程序中记录正在执行的操作,想要执行另一个操作前需要判断它和正在执行的操作间是否互斥。而正在实行的操作我们该如何记录,在哪里记录,是使用静态变量,还是使用单例。因为操作有很多,所以如果来记录需要的信息至关重要。
这个正式添加操作执行管理器的重要作用。每个操作必须调用指定的方法才可以执行该操作,而我们就是用操作执行管理器去调用该方法,在操作执行管理器中记录正在执行的操作的信息。这样我们使所有的操作在一个地方执行,而且要在进行验证后再执行。这样可以解决问题了。
此外增加操作执行管理器还有其他的作用,
(1) 记录UNDO/REDO信息
(2) 操作记录LOG信息的方法在这里调用
(3) 其他的一些跟操作相关的功能