Java 中单例模式的替代方案,而不是依赖注入或观察者

Alternative to singleton pattern in Java other than dependency injection or observer

我正在制作一个 JavaFX 应用程序,它有四个根控制器。这些中的每一个都控制选项卡上的视图。 select 选项卡没有控制器,或者因为它除了 select 显示四个视图中的哪一个之外没有其他功能。这些控制器需要访问相同的数据,并且它们都可以修改这些数据。

我能看到以可读的方式实现此目的的唯一方法是使用静态单例,它存储可由所有四个控制器访问和修改的状态。出于显而易见的原因,这并不理想。

我不能使用依赖注入,因为所有的控制器都是在应用程序启动时初始化的,它们不是由彼此创建的。出于同样的原因,我也不能使用观察者模式。他们无法获得彼此的引用,因此无法相互观察。据我所知,Java 中没有广播通知系统,这很烦人,因为它是一个解决方案。有没有我可以使用的另一种模式,或者我可以通过某种方式使这些模式之一起作用?

选项卡的 fxml 文件作为内容加载到选项卡窗格中。这样做是为了让我可以为每个选项卡设置不同的控制器。然后 start 方法加载选项卡窗格 fxml。

<TabPane prefHeight="200.0" prefWidth="200.0"   tabClosingPolicy="UNAVAILABLE" BorderPane.alignment="CENTER">
        <tabs>
          <Tab text="Data &amp; Statistics">
            <content>
              <fx:include fx:id="dataViewTabControl" source="DataViewTabControl.fxml" />
            </content>
          </Tab>
            . . .

您可以通过在 FXMLLoader 上设置控制器工厂来有效地使用依赖注入。这允许您控制如何为指定控制器 class fx:controller 属性的 FXML 文件创建控制器实例。控制器工厂用于包含的 FXML 文件以及主 FXML 文件。

因此,您可以使用应用程序状态创建一个模型 class,从控制器观察和修改该状态,并创建一个在所有控制器之间共享的实例。

定义您的模型:

public class Model { /* ... */ }

因此,如果您使用控制器定义 DataViewTabControl.fxml 文件,例如:

<?xml version="1.0" encoding="UTF-8"?>

<!-- imports etc -->

<SomeRootPane fx:controller="com.example.DataViewTabController">

    <!-- ... -->

</SomeRootPane>

并在构造函数中定义 DataViewTabController 以获取 Model 参数:

public class DataViewTabController {

    private final Model model ;

    public DataViewTabController(Model model) {
        this.model = model ;
    }

}

现在,当您加载主 FXML 文件以及任何其他可能具有将模型作为构造函数参数的控制器或 <fx:include> 执行相同操作的控制器的文件时,请使用调用正确的构造函数:

Model model = new Model();

FXMLLoader loader = new FXMLLoader(getClass().getResource("/path/to/fxml/file"));

loader.setControllerFactory((Class<?> controllerType) -> {
    try {
        for (Constructor<?> con : controllerType.getConstructors()) {
            if (con.getParameterCount() == 1 && con.getParameterTypes()[0]==Model.class) {
                return con.newInstance(model);
            }
        }
        // no suitable constructor found: just return default instance
        return controllerType.newInstance();
    } catch (Exception e) {
        System.err.println("Warning: could not load controller");
        e.printStackTrace(System.err);
        return null ;
    }
});

现在您的所有控制器都可以访问同一个 Model 实例,因此您可以使用通常的 MVC/Observer 模式来更改数据并响应数据的更改。如果您的模型 class 使用 JavaFX property API.

,这将特别容易