设计模式 设计模式 通过Java Swing看透MVC设计模式 代长亚 2024-10-31 2025-09-05 通过Java Swing看透MVC设计模式 一、引言 (一)GUI设计思想的启发 在现实世界中,一个简单的电脑键盘按键,如按钮,就体现了GUI设计的规则。它由动作特性(如可被按下)和表现(如代表的字母)两部分构成。这种设计思想可应用于软件开发,例如Model/View/Controller(MVC)设计模式。MVC设计模式鼓励重用,减轻设计工作的时间和难度,就像通过改变按钮表面的字母就能制造出“不同”的按钮,而无需重新设计整个按钮结构。
(二)MVC与Java Swing的关系 MVC设计模式通常用于设计整个用户界面(GUI),而Java Foundation Class(JFC)中的Swing组件将MVC设计模式应用于单个组件。如表格(JTable)、树(JTree)、组合下拉列表框(JComboBox)等组件都有各自的Model、View和Controller,且这些部分可以独立改变,即使组件正在被使用,这使得开发GUI界面的工具包非常灵活。
二、MVC设计模式概述 (一)Model
功能与特性
Model代表组件状态和低级行为,管理自身状态并处理所有对状态的操作。它不知道使用自己的View和Controller是谁,系统维护其与View的关系,当Model发生改变时,系统负责通知相应的View。例如,在一个游戏角色的模型中,它包含角色的生命值、攻击力等状态信息,以及升级、受伤等操作这些状态的方法。
(二)View
功能与特性
View负责管理Model所含数据的视觉呈现。一个Model可以有多个View,但在Swing中这种情况较少。比如,在一个数据可视化系统中,同一组数据可以通过柱状图、折线图等不同的View来展示给用户。
(三)Controller
功能与特性
Controller管理Model和用户之间的交互控制,提供处理Model状态变化情况的方法。以游戏中的角色控制为例,Controller接收用户的输入(如键盘按键、鼠标点击),并根据这些输入改变角色Model的状态(如移动、攻击)。
(四)MVC在按钮中的示例(配图:一个简单的按钮示意图,标注出Model、View/Controller部分) 用键盘按钮类比,Model就像按钮的整个机械装置,负责按钮的行为逻辑,如是否被按下、是否可用等状态的管理;View/Controller则如同按钮的表面部分,负责按钮的外观显示以及接收用户对按钮的操作(如点击、鼠标悬停等)。
关联Model和View/Controller的代码分析
Button类是Model和View/Controller之间的黏合剂。每个按钮组件都与一个Model和一个Controller关联,Model定义按钮行为,View/Controller定义按钮表现,应用程序可随时改变这些关联。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 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(); }
上述代码中,setModel
方法用于设置按钮的Model。当设置新的Model时,会先移除旧Model的相关监听器,然后添加新Model的监听器,并更新按钮状态。setUI
方法用于设置按钮的View/Controller,类似地,会先卸载旧的UI,再安装新的UI并更新按钮。updateButton
方法则通过调用invalidate
来触发按钮的重绘,确保按钮的显示与新的Model和View/Controller设置保持一致。
状态信息与事件通知代码分析
ButtonModel维护按钮的状态信息,如是否被按下(pressed)、是否“武装上了”(armed)、是否被选择(selected),这些都是布尔类型值。
1 2 3 4 5 6 7 8 private boolean boolPressed = false ;public boolean isPressed () { return boolPressed; } public void setPressed (boolean boolPressed) { this .boolPressed = boolPressed; fireChangeEvent(new ChangeEvent (button)); }
以上代码展示了pressed
状态的实现。isPressed
方法用于查询按钮是否被按下,setPressed
方法不仅设置按钮的按下状态,还会通过fireChangeEvent
方法触发状态改变事件,通知其他监听器按钮状态发生了变化。
ButtonModel还负责通知其他对象(事件监听器)感兴趣的事件,通过维护一个监听器列表来实现。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 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); } }
addChangeListener
和removeChangeListener
方法用于添加和移除监听器,fireChangeEvent
方法在状态改变时遍历监听器列表,调用每个监听器的stateChanged
方法,通知它们按钮状态发生了改变。(配图:一个按钮状态变化的流程图,展示从用户操作到Model状态改变,再到通知监听器的过程)
构建表示层与事件处理代码分析
ButtonUI负责构建按钮的表示层,默认情况下仅用背景色画一个矩形。
1 2 3 4 5 6 7 8 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); }
update
方法可用于在按钮状态改变时进行一些预处理,paint
方法则负责绘制按钮的外观,根据按钮的大小和背景色绘制一个矩形。
ButtonUI不直接处理AWT事件,而是通过定制的事件监听器将低级的AWT事件翻译为高级的Button模型期望的语义事件。
1 2 3 4 5 6 7 8 9 10 11 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); }
installUI
方法在按钮安装时添加相关事件监听器,uninstallUI
方法在按钮卸载时移除这些监听器。(配图:一个按钮绘制过程的示意图,展示从ButtonUI的paint方法到实际在屏幕上显示按钮的过程)
事件处理逻辑代码分析
ButtonUIListener帮助Button类将鼠标或键盘输入转换为对按钮模型的操作,实现了MouseListener、MouseMotionListener和ChangeListener接口。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 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(); }
mouseDragged
方法在鼠标拖动时,根据按钮Model的按下状态和鼠标位置更新按钮的“武装”状态;mousePressed
方法在鼠标按下时设置按钮的按下和“武装”状态;mouseReleased
方法在鼠标释放时重置按钮的按下和“武装”状态;stateChanged
方法在按钮状态改变时(如通过代码设置按钮状态),调用按钮的repaint
方法重绘按钮,以反映最新状态。
四、总结 通过对Java Swing中Button组件的MVC设计模式分析,我们看到了MVC在单个组件中的实现细节。在实际使用中,我们无需深入了解其底层实现即可方便地使用Swing组件,因为它们提供了默认的Model、View和Controller。然而,当我们自己开发组件时,MVC思想的强大之处就会显现出来,它能帮助我们构建出结构清晰、易于维护和扩展的软件系统。这种设计模式使得组件的各个部分职责分明,Model专注于数据和状态管理,View专注于显示,Controller专注于用户交互控制,它们之间的低耦合性保证了系统的灵活性和可维护性,为开发高质量的GUI应用提供了坚实的基础。