Java SE应用程序设计与MVC.doc_第1页
Java SE应用程序设计与MVC.doc_第2页
Java SE应用程序设计与MVC.doc_第3页
Java SE应用程序设计与MVC.doc_第4页
Java SE应用程序设计与MVC.doc_第5页
已阅读5页,还剩19页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

Java SE应用程序设计与MVC内容 - 什么是模型-视图-控制器(MVC)? - MVC组件之间的交互 - 修改了MVC设计 - 使用修改的MVC - 问题与应用设计 - 常用Swing组件监听器 - 结论 什么是模型-视图-控制器(MVC)?如果你已经编程的图形用户界面(GUI)在过去10年左右的图书馆,你有可能遇到的模型 - 视图 - 控制器(MVC)设计。 MVC最早是由引进特里夫Reenskaug ,一个Smalltalk的开发者在施乐帕洛阿尔托研究中心在1979年,并有助于去耦数据访问和业务逻辑从它显示给用户的方式。 更精确地说,MVC可细分为三个要素:模型 -模型表示数据和支配访问这个数据的更新规则。 在企业软件,模型经常作为一个现实世界的过程的软件近似。视图 -视图呈现模型的内容。 它指定了数据模型究竟应该如何呈现。 如果模型数据的变化,根据需要的视图必须更新它的介绍。 这可以通过使用推模式 , 即认为其自身注册到模型更改通知,或拉模式 ,其中的观点负责调用模型时,它需要检索最新的数据来实现。控制器 -控制器转换的观点付诸行动,该模型将执行用户的交互。 在一个独立的GUI客户端,用户交互可能是按钮单击或菜单选择,而在企业的Web应用程序,它们将显示为GET和POST HTTP请求。 根据上下文,控制器还可以选择一个新的观点 - 例如,结果的网页 - 呈现给用户。 MVC组件之间的交互本节将仔细看看一种方式来实现图1中的在应用程序的上下文中的Java平台标准版6 (Java SE 6中)。 一旦模型,视图和控制器对象被实例化,发生以下情况:1 该视图注册为模型上的监听器。 任何更改模型的基础数据直接导致广播变更通知,该观点得到。 这是前面所述的推模型的一个例子。 请注意,该模型是不知道的视图或控制器 - 它只是改变广播通知给所有感兴趣的听众。2 该控制器绑定到视图。 这通常意味着,那些在视图上执行的任何用户操作会在控制器类中调用一个注册的侦听器方法。3 所述控制器被赋予一个引用到底层模型。一旦用户与视图进行交互时,将发生以下操作:4 该观点认识到,一个GUI操作 - 例如,按下一个按钮或拖动滚动条 - 已发生,使用注册时,这样的动作发生时要调用的侦听器方法。5 该视图调用控制器上适当的方法。6 控制器访问模型,有可能更新它在适当的用户动作的方法。7 如果模型已经改变,它更改通知感兴趣的听众,如视图。 在某些架构中,控制器也可能是负责更新的视图。 这是常见的基于Java技术的企业应用程序。图2更详细地显示这种相互作用。图2:Java SE应用程序使用MVC 正如本文前面所提到的,该模型不带引用的观点,而是使用一个事件通知模型来通知改变的有关方面。 其中一个这样强大的设计所带来的后果是,很多观点可以有相同的底层模型。 当数据模型发生变化时,每个视图是由一个属性更改事件通知,并可以相应地进行自我更新。 例如,图3显示了使用相同的数据模型两种看法。 图3。 多个视图使用相同的型号 修改了MVC设计最近的一个实现MVC设计场所的模型和视图之间的控制器。 本设计中,这是常见的苹果可可框架中,示于图4。 图4。 一个MVC设计放置控制器模型和视图之间 这样的设计和更传统的版本的MVC之间的主要区别是,在模型对象的状态变化的通知是通过控制器传送到视图。 因此,控制器介导数据的模型和视图对象之间在两个方向上的流动。 查看对象,一如既往地使用控制器用户操作转化为对模型属性的更新。 此外,改变模型状态传达通过应用程序的控制器对象来查看对象。因此,当所有三个组件被实例化,视图和模型都将注册到控制器。 一旦用户与视图进行交互,该事件几乎是一样的:8 该观点认识到,一个GUI操作 - 例如,按下一个按钮或拖动滚动条 - 已发生,使用注册时,这样的动作发生时要调用的侦听器方法。9 该视图调用控制器上适当的方法。10 控制器访问模型,有可能更新它在适当的用户动作的方法。11 如果模型已经改变,它更改通知感兴趣的听众。 然而,在这种情况下,该变化被发送到控制器。为什么采用这样的设计? 使用这种改良的MVC有助于从视图中更完全解耦模型。 在这种情况下,控制器可以决定,它希望找到与控制器注册一个或多个模型的模型属性。 此外,它也能提供这种效果对于注册在其一个或多个视图模型的属性更改的方法。用改进的MVC本节中的文章将告诉您如何将这个设计付诸实践,开始与模型。 假设你想使用带有五个属性一个简单的显示模式来绘制一些文字。 代码示例1所示,你可以用它来创建这样一个模型一个简单的组件。代码示例1公共类TextElementModel扩展AbstractModel 私人字符串文本; 私人字体字体; 私人范围整数x; 私人整数Y; 私人整数不透明; 私人整数旋转; / * *可用于设置或重置模型的方法 *默认状态 * / 公共无效的initdefault() setOpacity(89); setRotation(0); 的setText(“样本文字”); 个setFont(新Font(“宋体”,Font.BOLD,24); setX的(50); SETY(50); / /访问器 公共字符串的getText() 返回文本; 公共无效的setText(字符串文本) 串oldText = this.text; this.text =文本; firePropertyChange为( DefaultController.ELEMENT_TEXT_PROPERTY, oldText,文本); 公众字体的getFont() 返回字体; 公共无效的setFont(Font字体) 字体oldFont = this.font; this.font =字体; firePropertyChange为( DefaultController.ELEMENT_FONT_PROPERTY, oldFont,字体); / /对资产的剩余可访问被忽略了。 请注意,访问器的其余部分遵循标准的JavaBeans模型,虽然它们在代码示例1省略。 作为参考,代码示例2显示了底层AbstractModel类,它只是使用javax.beans.PropertyChangeSupport类注册,注销,并通知更改模型的兴趣的听众。代码示例2公共抽象类AbstractModel 保护PropertyChangeSupport propertyChangeSupport; 公共AbstractModel() propertyChangeSupport =新PropertyChangeSupport(本); 公共无效addPropertyChangeListener(PropertyChangeListener的监听器) propertyChangeSupport.addPropertyChangeListener(监听器); 公共无效removePropertyChangeListener(PropertyChangeListener的监听器) propertyChangeSupport.removePropertyChangeListener(监听器); 受保护的firePropertyChange无效(字符串propertyName的,对象OLDVALUE,对象newValue)以 propertyChangeSupport.firePropertyChange(属性,OLDVALUE,为newValue); 该控制器模型和视图之间是控制器。 首先,来看看为抽象超类控制器的代码,如代码示例3。代码示例3公共抽象类一个AbstractController实现的PropertyChangeListener 私人的ArrayList registeredViews; 私人的ArrayList registeredModels; 公众一个AbstractController() registeredViews =新的ArrayList (); registeredModels =新的ArrayList (); 公共无效addModel(AbstractModel模型) registeredModels.add(模型); model.addPropertyChangeListener(本); 公共无效removeModel(AbstractModel模型) registeredModels.remove(模型); model.removePropertyChangeListener(本); 公共无效addView(AbstractViewPanel视图) registeredViews.add(视图); 公共无效removeView(AbstractViewPanel视图) registeredViews.remove(视图); / /使用这个来观察属性变更的登记模式 / /和传播他们到各方面的意见。 公共无效为propertyChange(PropertyChangeEvent中EVT) 对于(AbstractViewPanel观点:registeredViews) view.modelPropertyChange(EVT); / * *这是一个子类可以在调用一个便利的方法 *火属性更改回款。 该方法 *使用反射来检查每个模型类的 *以确定它是否是财产的所有者 *有问题。 如果不是,一个的NoSuchMethodException被抛出, *该方法忽略。 * * 参数propertyName的=属性的名称。 * 参数为newValue =一个表示新的值对象 *该物业。 * / 保护无效setModelProperty(字符串propertyName的,对象newValue)以 对于(AbstractModel型号:registeredModels) 尝试 方法方法= model.getClass()。 实现getMethod(“设置”+ propertyName的,新的Class newValue.getClass() ); method.invoke(模型,为newValue); 赶上(例外) / /处理异常。 在AbstractController类包含两个ArrayList的对象,这是用来跟踪已注册的模型和视图。 需要注意的是,每当一个模型被注册,控制器也将自己注册为模型上的属性更改侦听器。 这样一来,每当一个模型改变其状态, propertyChange()方法被调用时,控制器会通过这个事件到适当的意见。最后一个方法, setModelProperty()采用了一些魔法来完成自己的工作。 为了保持从控制器完全解耦模型,本文中的代码示例都采用了Java的反射API 。 在这种情况下,当调用此方法时具有所需属性的名字,你寻找通过已注册的模式,以确定哪一个包含了相应的方法。 一旦你找到它,你使用新值调用该方法。 如果不会调用此方法时, getMethod()将抛出一个NoSuchMethodException ,该异常处理程序忽略,允许for-循环继续。代码示例4显示了默认控制器类的源代码。 这个类包含调用视图的GUI事件监听器唯一属性的常量和方法。代码示例4公共类默认控制器扩展了一个AbstractController 公共静态最终字符串ELEMENT_TEXT_PROPERTY =“文本”; 公共静态最终字符串ELEMENT_FONT_PROPERTY =“字体”; 公共静态最终字符串ELEMENT_X_PROPERTY =“X”; 公共静态最终字符串ELEMENT_Y_PROPERTY =“Y”; 公共静态最终字符串ELEMENT_OPACITY_PROPERTY =“不透明度”; 公共静态最终字符串ELEMENT_ROTATION_PROPERTY =“旋转”; / /代码省略 公共无效changeElementText(字符串以newText) setModelProperty(ELEMENT_TEXT_PROPERTY,以newText); 公共无效changeElementFont(字体newFont) setModelProperty(ELEMENT_FONT_PROPERTY,newFont); 公共无效changeElementXPosition(INT下一页末) setModelProperty(ELEMENT_X_PROPERTY,下一页末); 公共无效changeElementYPosition(INT newY) setModelProperty(ELEMENT_Y_PROPERTY,newY); 公共无效changeElementOpacity(INT newOpacity) setModelProperty(ELEMENT_OPACITY_PROPERTY,newOpacity); 公共无效changeElementRotation(INT newRotation) setModelProperty(ELEMENT_ROTATION_PROPERTY,newRotation); 查看这个例子有两个观点显示来自模型的数据:一个属性编辑器视图和图形页面视图。 这两者都是一个的实现中JPanel ,插入到任何一个JDialog或JFrame 。 该对话框允许用户更新模型值,和帧面板简单地反映了变化的最终文本显示。 本文使用内置这个例子的作者的NetBeans Swing GUI的生成器 ,以前称为Matisse项目,设计出图形用户界面形式。代码示例5显示了属性编辑器视图,更有趣的两个源代码。 第一部分被简单地倾注到组件,它在大多数情况下是自动生成的初始化NetBeans的集成开发环境(IDE)在initComponents()方法。 所有这些部分被省略,但存在于下载的代码。 在这种情况下,创建自定义模式-你需要执行任何其它初始化JSpinner和JSlider对象或添加DocumentListeners到JTextField组件-在处理localInitialization()方法。代码示例5公共PropertiesViewPanel(默认控制器控制器) this.controller =控制器; 位于initComponents(); localInitialization(); / / / * *用于提供Swing组件的本地初始化 *在NetBeans自动代码生成器之外 * / 公共无效localInitialization() opacitySpinner.setModel(新SpinnerNumberModel(100,0,100,1); opacitySlider.setModel(新DefaultBoundedRangeModel(100,0,0,100); rotationSpinner.setModel(新SpinnerNumberModel(0,-180,180,1); rotationSlider.setModel(新DefaultBoundedRangeModel(0,0,-180,180); text.getDocument()。addDocumentListener(新DocumentListener() 公共无效的insertUpdate(的DocumentEvent E) textDocumentChanged(E); 公共无效的removeUpdate(的DocumentEvent E) textDocumentChanged(E); 公共无效的changedUpdate(的DocumentEvent E) textDocumentChanged(E); ); / / 需要注意的是自动生成的NetBeans IDE中的代码在源代码折叠,这样,当不需要它的开发者可以折叠每一部分: / / / / 如果您使用NetBeans IDE中,这种做法是极力推荐。的第二部分PropertiesViewPanel类专门处理模型。 代码示例6,一个modelPropertyChange()方法被调用时由控制器每当模型报告状态改变。代码示例6/ / 公共无效modelPropertyChange(最终的PropertyChangeEvent EVT) 如果(evt.getPropertyName()。等于( DefaultController.ELEMENT_X_PROPERTY) 串newStringValue = evt.getNewValue()的toString(); xPositionTextField.setText(newStringValue); 的 else if (evt.getPropertyName()。等于( DefaultController.ELEMENT_Y_PROPERTY) 串newStringValue = evt.getNewValue()的toString(); yPositionTextField.setText(newStringValue); 的 else if (evt.getPropertyName()。等于( DefaultController.ELEMENT_OPACITY_PROPERTY) 整型newIntegerValue =(整数)evt.getNewValue(); opacitySpinner.setValue(newIntegerValue); opacitySlider.setValue(newIntegerValue); 的 else if (evt.getPropertyName()。等于( DefaultController.ELEMENT_ROTATION_PROPERTY) 整型newIntegerValue =(整数)evt.getNewValue(); rotationSpinner.setValue(newIntegerValue); rotationSlider.setValue(newIntegerValue); 的 else if (evt.getPropertyName()。等于( DefaultController.ELEMENT_TEXT_PROPERTY) 串newStringValue = evt.getNewValue()的toString(); text.setText(newStringValue); 的 else if (evt.getPropertyName()。等于( DefaultController.ELEMENT_FONT_PROPERTY) 字体F =(字体)evt.getNewValue(); 串fontString = f.getFontName()+“”+ f.getSize(); font.setText(fontString); currentFont = F; 代码省略/ /余 / / 同样,这段代码示例省略了类似于所示的部分代码的部分。最后一个部分由GUI事件侦听器。 代码示例7中包含的听众,每当GUI事件发生被称为,如推动Change Font按键或Opacity微调按钮或重置在任何一个文本字段中的文本。 这在很大程度上是事件侦听器,大部分的Swing开发人员已经熟悉了。 如果您使用NetBeans IDE中,你会看到IDE自动生成许多这些使用GUI开发。代码示例7 / / / /代码省略 私人无效yPositionTextFieldFocusLost(java.awt.event.FocusEvent EVT) 尝试 controller.changeElementYPosition( 的Integer.parseInt(yPositionTextField.getText(); 赶上(例外五) / /处理异常。 私人无效yPositionTextFieldActionPerformed(java.awt.event.ActionEvent EVT) 尝试 controller.changeElementYPosition( 的Integer.parseInt(yPositionTextField.getText(); 赶上(例外五) / /处理异常。 / /代码省略 - 为xPosition代码 / /是几乎一样的yPosition。 私人无效changeFontButtonActionPerformed(java.awt.event.ActionEvent EVT) JFontChooserDialog fontChooser =新 JFontChooserDialog(对话)this.getTopLevelAncestor(); fontChooser.setSelectedFont(currentFont); fontChooser.setVisible(真); 字体returnedFont = fontChooser.getSelectedFont(); 如果(returnedFont!= NULL) controller.changeElementFont(returnedFont); 私人无效opacitySliderStateChanged(javax.swing.event.ChangeEvent EVT) controller.changeElementOpacity(int)的opacitySlider.getValue(); 私人无效rotationSliderStateChanged(javax.swing.event.ChangeEvent EVT) controller.changeElementRotation(int)的rotationSlider.getValue(); 私人无效opacitySpinnerStateChanged(javax.swing.event.ChangeEvent EVT) controller.changeElementOpacity(整数)opacitySpinner.getValue(); 私人无效rotationSpinnerStateChanged(javax.swing.event.ChangeEvent EVT) controller.changeElementRotation(整数)rotationSpinner.getValue(); 私人无效textDocumentChanged(EVT的DocumentEvent) 文档文件= evt.getDocument(); 尝试 controller.changeElementText(document.getText(0, document.getLength(); 赶上(BadLocationException前) / /处理异常。 / / 问题与应用设计一旦应用程序启动并运行,你马上碰到一个问题。 考虑下面的事件链:12 在一个视图中的Swing组件接收到的变化,想必从用户操作。13 适当的控制器方法被调用。14 该模型被更新。 它会通知它的属性更改的控制器。15 视图接收到改变事件从控制器并尝试重新设置适当的摆动分量的值。16 适当的控制器的方法被调用时,该模型被再次更新。在这一点上,任何三种不同的情景可以发生,这取决于你用什么Swing组件,以及如何强大你的模型。Swing组件,促使最初的变化拒绝自我更新的第二次,并指出它的属性状态,不能再次更新,而它在通知初始状态变化的听众的过程。 当您使用Swing的文本组件这主要发生。该模型指出,第二次更新的值相匹配的第一个,其目前的价值,并拒绝发送更改通知。 这始终是一个安全的编程习惯,如果你使用的是自动发生PropertyChangeSupport中提供的类java.beans包。 但是,它不保持模型接收冗余更新。没有保障措施无论是模型或Swing组件,程序进入了死循环。出现此问题的原因Swing组件是自主的。 例如,如果用户按下的向上箭头发生JSpinner组件中PropertiesViewPanel ,由一个递增微调的价值? 后的值被更新,一个GUI事件侦听器方法,它正在侦听价值变动被调用时, opacitySpinnerStateChanged()这反过来又调用控制器,然后更新模型中相应的属性。与传统的MVC设计,视图仍然会包含以前的值,并在模型中的改变会更新视图的当前值。 但是,没有必要更新Swing组件,因为它已经本身重置为正确的值 - 它这样做之前就通过一个事件到控制器。你如何解决这个问题呢? 一种方式是写,告诉模型或控制器不能在这种情况下传播的变化通知机制,但是这不是一个好主意。 请记住,不止一个视图可以监听在模型上的变化。 如果您关闭了更改通知模型,没有其他的听众,包括其他的意见,将收到的改变。 此外,在同一个视图中的其他组件可能依赖属性更改通知,采用了滑盖和旋转器组合,例如。理想情况下,每个Swing组件会知道它的当前值与该视图正试图将其设置为值。 如果它们匹配,没有变更通知将被发送。 然而,一些Swing组件包括这样的逻辑,和别人不一样。 一个可能的解决方案是检查对存储在Swing组件的当前值模型的传入改变值。 如果它们是相同的,就没有必要重新设置摆动分量的值。代码示例8显示的更新modelPropertyChange()方法来演示这种方法。代码示例8公共无效modelPropertyChange(最终的PropertyChangeEvent EVT) 如果(evt.getPropertyName()。的equals(DefaultController.ELEMENT_X_PROPERTY) 串newStringValue = evt.getNewValue()的toString(); 如果(!xPositionTextField.getText()。的equals(newStringValue) xPositionTextField.setText(newStringValue); / /剩余的代码省略 最后一个例子,它使用两个委托的观点中,示于图5。 第二个代表利用了的Java 2D的库来显示文字,这已经超出了本文的范围。 但是,源代码是相对容易跟随和包含在下载的源代码。图5。 两个视图连接到单个模型 常见的Swing组件监听器正如这篇文章所贯穿所示,每个代表或控制器的初始化的一部分要求你订阅各种Swing组件的事件。然而,个别Swing组件通常可以产生多个事件类型。 表1列出了一些常见的侦听器方法。表1中。 常见的Swing事件侦听器方法事件监视Swing事件侦听器方法JButton压制; JCheckBox压制;JRadioButton压;JToggleButton切换。JButton.addActionListener(java.awt.event.ActionListener)JSpinner值改变。JSpinner.addChangeListener(javax.swing.event.ChangeListener)JTextField ,JFormattedTextField ,JPasswordField或JTextArea字符(s)被添加,更改或删除。JTextField.getDocument().addDocumentListener(javax.swing.event.DocumentListener)JTextField返回按钮被按下,或者对焦切换到另一个组件。JTextField.addActionListener(java.awt.event.ActionListener)JComboBox新条目被选中。 从列表中。JComboBox.addActionListener(java.awt.event.ActionListener)JComboBox编辑器(通常是文本字段)字符添加,更改或删除。JTextField editor = (JTextField)comboBox.getEditor().getEditorComponent(); editor.getDocument().addDocumentListener(javax.swing.event.DocumentListener)JComboBox返回按钮被按下。JComboBox.addActionListener(java.awt.event.ActionListener)JComboBox弹出式菜单即将成为可见的,不可见的,或不选择被取消。JComboBox.addPopupMenuListener(javax.swing.event.PopupMenuListener)JList新条目被选中。J

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论