14

我正在尝试研究 JavaFX,因为我想将它用作我的程序的 GUI。我的问题本质上是一个概念问题:

迄今为止,我的程序主要是 MVC 模式的“模型”部分;也就是说,我的几乎所有代码都是类意义上的抽象的面向对象表示,并且所有这些代码都是逻辑代码。

由于我不想成为我的程序的唯一用户,我想添加 MVC 的“视图”部分,以便人们可以轻松地使用和操作我的程序的“模型”部分。为此,我想使用 JavaFX。

在我的“模型”类中,我显然使用了 Java 集合 API 中的各种列表、地图和其他类。为了让我的程序的用户操作这些底层列表和地图,我想在 JavaFX 中使用 Observable(List/Map) 接口。

一个使情况更清晰的具体示例:

假设我有一个MachineMonitor类,它每 3 分钟检查一次机器的某些属性,例如连接是否仍然良好,齿轮转动的速度等。如果满足某些不等式(比如说齿轮已降至 1 转/秒的速率)MachineMonitor触发 RestartMachineEvent。

目前我使用 ArrayList< MachineMonitor > 来跟踪所有单个MachineMonitor的。现在扩展到 MVC 的“视图”部分,我希望用户能够操作显示 MachineMonitor 列表的 TableView,以便他们可以创建和删除新的MachineMonitor以监控各种机器。

这样我就可以跟踪我的程序的用户想要做什么(例如,为机器#5 创建一个MachineMonitor,检查齿轮的转数/秒是否低于 0.5)我使用 ObservableList< MachineMonitor > 作为TableView 的基础列表。

链接我的程序的“模型”和“视图”的最简单方法是将“模型”类更改为具有 ObservableList< MachineMonitor > 而不是 ArrayList< MachineMonitor > 但是(进入问题的主题)我觉得这很混乱,因为它混合了“模型”和“视图”代码。

一种天真的方法是对 TableView 使用 ObservableList< MachineMonitor > 并保留我的 ArrayList< MachineMonitor > 的使用。但是,根据 JavaFX 规范,对 ObservableList< MachineMonitor > 所做的更改不会影响基础列表。

鉴于此,解决这个难题的最佳方法是为 ObservableList< MachineMonitor > 创建一个 ChangeListener,它将对 ObservableList< MachineMonitor >所做的更改“传播”到底层“模型”ArrayList< MachineMonitor >?也许把它放在一个名为 MachineMonitorController 的类中?

这种临时解决方案似乎非常混乱且不理想。

我的问题是:在这种情况下,保持“模型”和“视图”之间几乎完全分离的最佳方法是什么?

4

2 回答 2

12

简而言之,我不认为使用 ObservableList 会破坏 MVC 合同。

其余的,你可以随心所欲地阅读或不阅读,因为它很长很烦人。

建筑图案背景

Observables在 MVC 风格架构中很有用,因为它们提供了一种通过松散耦合在 MVC 组件之间来回馈送数据的方式,其中模型和视图类不需要直接相互引用,而是可以处理一些共享数据通信数据流的模型。Observable 模式和 MVC 风格的架构概念几乎同时起源于 Xerox PARC,这并非巧合——它们是相互关联的。

正如Martin Fowler 的 GUI 体系​​结构中所述,构建 GUI 有许多不同的方法。MVC 只是其中之一,是它们的祖父。很好地理解 MVC(它经常被误解)并且 MVC 概念在很多地方都适用。对于您的应用程序,您应该使用最适合您的系统,而不是严格遵循给定的模式(除非您使用的是强制执行给定模式的特定框架),并且还可以在应用程序中采用不同的模式而不是试图硬拉一切都在一个单一的概念框架中。

Java Bean 是几乎所有 Java 程序的基本组成部分。尽管传统上通常只在客户端应用程序中使用,但观察者模式通过,自创建以来一直是Java Bean 规范PropertyChangeListeners的一部分。JavaFX 的可观察和绑定元素是对早期工作的重做,从中学习以构建更方便使用且更易于理解的东西。或许,如果 JavaFX 可观察和绑定元素在 10 或 12 年前作为 JDK 的一部分存在,那么与几个纯 GUI 框架相比,这些概念将更广泛地用于更广泛的库和框架中。

建议

我建议考虑MVVM 模型和其他 GUI 架构。

如果你想要一个遵循模型、视图、演示者风格的简单框架,一定要试试 afterburner.fx

我认为架构的正确选择取决于您的应用程序、您的经验以及您尝试解决的问题的规模和复杂性。例如,如果您有一个分布式系统,那么您可以遵循REST 原则而不是(或除了)MVC。无论您选择哪种,架构都应该帮助您解决手头的问题(以及可能的未来问题),而不是相反。过度设计解决方案是一个常见的陷阱,而且很容易做到,所以尽量避免。

警告

需要考虑的一个警告是,可观察对象必然会通过副作用起作用,这些副作用可能难以推理并且可能与隔离的概念对立。JavaFX 提供了一些很好的工具,例如ReadOnlyObjectWrapperReadOnlyListWrapper,以帮助限制对可观察对象的影响(如果您愿意,可以控制损坏),因此它们不会在您的系统中运行异常。不计后果地使用这些工具(和不可变对象)。

从例子中学习

对于使用 observables 构建的简单 JavaFX 应用程序,请参阅tic-tac-toe

有关使用基于 FXML 的组件构建大型复杂 JavaFX 应用程序的好方法,请参阅SceneBuilder 和 SceneBuilderKit的源代码。源代码在JavaFX mercurial 源代码树中可用,只需查看并开始学习

阅读JavaFX UI 控件架构。检查 JavaFX 控件源代码(例如ButtonButtonSkinListViewListViewSkin)以了解如何使用 JavaFX 结构应用 MVC 等概念。基于该学习,尝试使用 JavaFX 控件框架提供的体系结构创建一些您自己的自定义控件。通常,当您构建自己的应用程序时,您不需要创建自己的控件(至少从 JavaFX Control派生的控件))。JavaFX 控件架构是专门为支持构建可重用控件库而设计的,因此它不一定适用于所有用途;相反,它提供了一种经过验证的方法来完成某些事情的具体演示。采用和调整经过验证的解决方案有助于确保您不会不必要地重新发明东西,并让您建立在坚实的基础上并从他人的试验中学习。

关于你的具体例子

我建议你去:

链接我的程序的“模型”和“视图”的最简单方法是将“模型”类更改为具有 ObservableList 而不是 ArrayList

也许使用 ReadOnlyListWrapper 将来自 MachineMonitor 的 ObservableList 暴露给外界,这样就没有任何东西可以过度修改它。

设置一些封装视图的其他结构(例如 ControlPanel 和 ControlPanelSkin),并为其提供对 MachineMonitors 的只读可观察列表的引用。ControlPanelSkin 可以封装一个 TableView、一个图形或任何您想要用于用户监控机器的可视化旋钮和小部件。

使用这种结构可以有效地将您的视图与模型隔离开来。该模型对 UI 完全一无所知,并且 ControlPanelSkin 实现可以更改为完全不同的视觉表示或技术,而无需更改核心 MachineMonitor 系统。

以上只是概述了一种通用方法,您需要针对您的具体示例对其进行调整。

于 2014-05-14T04:54:27.873 回答
11

我不同意ObservableList在你的“模型”类中使用违反 MVC 分离。AnObservableList是纯粹的数据表示;它是模型的一部分,而不是视图的一部分。我( 其他人在我的应用程序的所有层中的模型表示中使用 JavaFX 属性和集合。除其他事项外,我指出了我如何使用已(或至少可以)绑定到 JSF 的 JavaFX 属性。(我应该提到,并不是每个人都同意在服务器端使用 FX 属性的方法;但是我真的看不出有任何方法可以证明它们在某种程度上是视图的一部分。)

另外,如果你这样做

List<MachineMonitor> myNonObservableList = ... ;

ObservableList<MachineMonitor> myObservableList = FXCollections.observableList(myNonObservableList);
myObservableList.add(new MachineMonitor());

可观察列表由不可观察列表支持,因此更改也会发生myNonObservableList。因此,如果您愿意,可以使用这种方法。

于 2014-05-14T01:01:14.847 回答