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 & 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.
,这将特别容易
我正在制作一个 JavaFX 应用程序,它有四个根控制器。这些中的每一个都控制选项卡上的视图。 select 选项卡没有控制器,或者因为它除了 select 显示四个视图中的哪一个之外没有其他功能。这些控制器需要访问相同的数据,并且它们都可以修改这些数据。
我能看到以可读的方式实现此目的的唯一方法是使用静态单例,它存储可由所有四个控制器访问和修改的状态。出于显而易见的原因,这并不理想。
我不能使用依赖注入,因为所有的控制器都是在应用程序启动时初始化的,它们不是由彼此创建的。出于同样的原因,我也不能使用观察者模式。他们无法获得彼此的引用,因此无法相互观察。据我所知,Java 中没有广播通知系统,这很烦人,因为它是一个解决方案。有没有我可以使用的另一种模式,或者我可以通过某种方式使这些模式之一起作用?
选项卡的 fxml 文件作为内容加载到选项卡窗格中。这样做是为了让我可以为每个选项卡设置不同的控制器。然后 start 方法加载选项卡窗格 fxml。
<TabPane prefHeight="200.0" prefWidth="200.0" tabClosingPolicy="UNAVAILABLE" BorderPane.alignment="CENTER">
<tabs>
<Tab text="Data & 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.