在 MVC 模式中处理文本字段 SetOnAction 的正确方法是什么
What is the correct way to handle the Textfield SetOnAction in a MVC pattern
我在尝试让我的 TextField 连接到我在控制器 class 中定义的 ActionEvent 句柄时遇到问题。错误出现在java.lang.reflect.InvocationTargetException。我一直在尝试做的是在视图中创建我的控制器 class 的实例,然后使用 lamba 方法引用来调用控制器 class.
中的 handle 方法
查看Class
package converter;
import javafx.scene.control.RadioButton;
import javafx.scene.control.TextField;
import javafx.scene.control.ToggleGroup;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
public class View extends BorderPane{
private Controller control = new Controller(new Model(), new View());
//TextField
private TextField input = new TextField();
private TextField input2 = new TextField();
//RadioButton
private RadioButton distence = new RadioButton();
private RadioButton tempeture = new RadioButton();
private RadioButton weight = new RadioButton();
//ToggleGroup
private ToggleGroup group = new ToggleGroup();
public String getConversion()
{
return group.getSelectedToggle().getUserData().toString();
}
public double getInput()
{
return Double.parseDouble(input.getText());
}
public double getInput2()
{
return Double.parseDouble(input2.getText());
}
public void setInput(double value)
{
input.setText(Double.toString(value));
}
public void setInput2(double value)
{
input2.setText(Double.toString(value));
}
public View()
{
System.out.println(control);
//SetID
//input.setPromptText("Input");
//output.setPromptText("Output");
input.setId("input");
input2.setId("input2");
//SetUserData
distence.setUserData("dist");
tempeture.setUserData("temp");
weight.setUserData("weight");
//Set Label
distence.setText("Mile and Kilometer");
tempeture.setText("Celsius and Fahrenheit");
weight.setText("Pounds and Kilograms");
//SetGroup
distence.setToggleGroup(group);
tempeture.setToggleGroup(group);
weight.setToggleGroup(group);
//Add TextField ActionEvent
input.setOnAction(control::handle);
input2.setOnAction(control::handle);
//Add Group Listener
group.selectedToggleProperty().addListener((ov, o , n) ->{
// System.out.println(n.getUserData().toString());
String tog = n.getUserData().toString();
if (tog.equals("dist")) {
input.setPromptText("Mile");
input2.setPromptText("Kilometer");
}else if(tog.equals("temp")) {
input.setPromptText("Fahrenheit");
input2.setPromptText("Celsius");
}else if(tog.equals("weight")) {
input.setPromptText("Pound");
input2.setPromptText("Kilogram");
}
});
StackPane left = new StackPane();
StackPane right = new StackPane();
VBox leftbox = new VBox(3);
VBox rightbox = new VBox(2);
leftbox.setSpacing(5);
rightbox.setSpacing(10);
leftbox.getChildren().addAll(distence, tempeture, weight);
left.getChildren().add(leftbox);
rightbox.getChildren().addAll(input, input2);
right.getChildren().add(rightbox);
this.setLeft(left);
this.setRight(right);
}
}
控制器Class
public class Controller implements EventHandler<ActionEvent>{
private Model model;
private View view;
public Controller(Model model, View view)
{
this.model = model;
this.view = view;
}
public Controller()
{
initalize();
}
public Controller initalize()
{
this.model = new Model();
this.view = new View();
return this;
}
@Override
public void handle(ActionEvent event)
{
String id = ((javafx.scene.Node)event.getSource()).getId();
String conversion = view.getConversion();
switch (id) {
case "input":
if(conversion.equals("dist")) {
view.setInput2(model.kilometer(view.getInput()));
break;
}else if(conversion.equals("temp")) {
view.setInput2(model.cToF(view.getInput()));
break;
}else if(conversion.equals("weight")) {
view.setInput2(model.kilogram(view.getInput()));
break;
}
case "input2":
if(conversion.equals("dist")) {
view.setInput(model.mile(view.getInput2()));
break;
}else if(conversion.equals("temp")) {
view.setInput(model.fToC(view.getInput2()));
break;
}else if(conversion.equals("weight")) {
view.setInput(model.pound(view.getInput2()));
break;
}
default:
break;
}
}
}
主要class
package converter;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class Main extends Application {
private View view;
@Override
public void init()
{
Model model = new Model();
view = new View();
new Controller(model, view);
}
@Override
public void start(Stage primaryStage) {
try {
primaryStage.setMinWidth(350);
primaryStage.setMinHeight(150);
primaryStage.setScene(new Scene(view));
primaryStage.show();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
堆栈跟踪
Exception in Application init method
java.lang.reflect.InvocationTargetException
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:78)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:567)
at javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplicationWithArgs(LauncherImpl.java:465)
at javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplication(LauncherImpl.java:364)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:78)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:567)
at java.base/sun.launcher.LauncherHelper$FXHelper.main(LauncherHelper.java:1071)
Caused by: java.lang.RuntimeException: Exception in Application init method
at javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:896)
at javafx.graphics/com.sun.javafx.application.LauncherImpl.lambda$launchApplication(LauncherImpl.java:196)
at java.base/java.lang.Thread.run(Thread.java:831)
Caused by: java.lang.WhosebugError
at javafx.graphics/javafx.scene.Node.getScene(Node.java:1148)
at javafx.graphics/javafx.scene.Node.updateCanReceiveFocus(Node.java:8502)
at javafx.graphics/javafx.scene.Node.setTreeVisible(Node.java:8420)
at javafx.graphics/javafx.scene.Node.updateTreeVisible(Node.java:8411)
at javafx.graphics/javafx.scene.Node.<init>(Node.java:2596)
at javafx.graphics/javafx.scene.Parent.<init>(Parent.java:1418)
at javafx.graphics/javafx.scene.layout.Region.<init>(Region.java:627)
at javafx.graphics/javafx.scene.layout.Pane.<init>(Pane.java:136)
at javafx.graphics/javafx.scene.layout.BorderPane.<init>(BorderPane.java:219)
at converter.View.<init>(View.java:52)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
Exception running application converter.Main
您正在此处创建循环依赖。控制器依赖于视图依赖于控制器。
这是糟糕的设计。
您应该实现控制反转并依赖于抽象。抽象要么在其构造中赋予视图,要么存在工厂或某些依赖注入框架。另一种选择是让您的视图发出控制器订阅的视图特定事件。有很多方法可以正确解决这个问题 - 循环依赖不是其中之一。
抽象应该是尽可能小的完全抽象(接口隔离原则),所以如果你需要一个处理程序,你可能有一个接口只有一个handle(event)
函数,没有更多。
这并不是禁止Controller实现handle函数,可能会违反单一职责原则,但这要看controller的问题和实现来决定。
在此处查看 SOLID 原则:Wikipedia SOLID
更新:您快完成了,只需将 setEventHandler 添加为视图 属性。
public void setEventHandler(@NotNull EventHandler handler) {
this.handler = handler;
updateHandler();
}
private void updateHandler() {
//Add TextField ActionEvent
input.setOnAction(handler::handle);
input2.setOnAction(handler::handle);
}
并将其添加到您的 Main::init
:
Controller controller = new Controller(model , view);
view.setEventHandler(controller);
您还必须修复控制器的默认构造函数。
您的异常发生是因为您在构造 View
实例时有无限递归:
public class View {
// ...
private Controller control = new Controller(new Model(), new View());
// ...
}
当您创建一个 View
实例时,您会尝试创建一个新的 View
实例(以传递给 Controller
构造函数),而后者又会创建一个新的 View
实例传递给 Controller
构造函数,等等。即使你在这里解决了依赖关系,你也希望这些对象引用同一个实例;您不想到处创建新实例。
MVC 有几种不同的变体。看起来您正在尝试实现“传统”MVC,其中:
- 视图观察模型,并在模型发生变化时进行更新
- 视图将用户对其封装的组件(例如文本字段)的操作委托给控制器
- 控制器更新模型
所以:
- 视图应该引用控制器和模型
- 控制器应该引用模型
- 模型应该对视图或控制器一无所知
我还建议不要让控制器实现任何 EventHandler
接口;只需定义处理用户输入所需的方法。为每个用户操作定义单独的方法,而不是使用一个单一的 handle()
方法和无穷无尽的 switch
或 if-else
语句。
所以像这样:
public class View {
private final Controller controller ;
private final Model model ;
private final TextField input ;
private final TextField input2 ;
// ...
public View(Model model, Controller controller) {
this.model = model ;
this.controller = controller ;
input = new TextField();
input2 = new TextField();
model.someProperty().addListener((obs, oldValue, newValue) -> {
/* update controls */
});
model.someOtherProperty().addListener((obs, oldValue, newValue) -> {
/* update controls */
});
input.setOnAction(event -> controller.handleInput(input.getText()));
input2.setOnAction(event -> controller.handleInput2(input2.getText()));
// layout etc
}
}
public class Controller {
public final Model model ;
public Controller(Model model) {
this.model = model ;
}
public void handleInput(String input) {
model.setSomeValue(input);
}
public void handleInput2(String input) {
model.setSomeOtherValue(input2);
}
// etc
}
然后你 assemble 使用类似
的代码
Model model = new Model();
Controller controller = new Controller(model);
View view = new View(model, controller);
有关完整示例,请参阅 。
我在尝试让我的 TextField 连接到我在控制器 class 中定义的 ActionEvent 句柄时遇到问题。错误出现在java.lang.reflect.InvocationTargetException。我一直在尝试做的是在视图中创建我的控制器 class 的实例,然后使用 lamba 方法引用来调用控制器 class.
中的 handle 方法查看Class
package converter;
import javafx.scene.control.RadioButton;
import javafx.scene.control.TextField;
import javafx.scene.control.ToggleGroup;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
public class View extends BorderPane{
private Controller control = new Controller(new Model(), new View());
//TextField
private TextField input = new TextField();
private TextField input2 = new TextField();
//RadioButton
private RadioButton distence = new RadioButton();
private RadioButton tempeture = new RadioButton();
private RadioButton weight = new RadioButton();
//ToggleGroup
private ToggleGroup group = new ToggleGroup();
public String getConversion()
{
return group.getSelectedToggle().getUserData().toString();
}
public double getInput()
{
return Double.parseDouble(input.getText());
}
public double getInput2()
{
return Double.parseDouble(input2.getText());
}
public void setInput(double value)
{
input.setText(Double.toString(value));
}
public void setInput2(double value)
{
input2.setText(Double.toString(value));
}
public View()
{
System.out.println(control);
//SetID
//input.setPromptText("Input");
//output.setPromptText("Output");
input.setId("input");
input2.setId("input2");
//SetUserData
distence.setUserData("dist");
tempeture.setUserData("temp");
weight.setUserData("weight");
//Set Label
distence.setText("Mile and Kilometer");
tempeture.setText("Celsius and Fahrenheit");
weight.setText("Pounds and Kilograms");
//SetGroup
distence.setToggleGroup(group);
tempeture.setToggleGroup(group);
weight.setToggleGroup(group);
//Add TextField ActionEvent
input.setOnAction(control::handle);
input2.setOnAction(control::handle);
//Add Group Listener
group.selectedToggleProperty().addListener((ov, o , n) ->{
// System.out.println(n.getUserData().toString());
String tog = n.getUserData().toString();
if (tog.equals("dist")) {
input.setPromptText("Mile");
input2.setPromptText("Kilometer");
}else if(tog.equals("temp")) {
input.setPromptText("Fahrenheit");
input2.setPromptText("Celsius");
}else if(tog.equals("weight")) {
input.setPromptText("Pound");
input2.setPromptText("Kilogram");
}
});
StackPane left = new StackPane();
StackPane right = new StackPane();
VBox leftbox = new VBox(3);
VBox rightbox = new VBox(2);
leftbox.setSpacing(5);
rightbox.setSpacing(10);
leftbox.getChildren().addAll(distence, tempeture, weight);
left.getChildren().add(leftbox);
rightbox.getChildren().addAll(input, input2);
right.getChildren().add(rightbox);
this.setLeft(left);
this.setRight(right);
}
}
控制器Class
public class Controller implements EventHandler<ActionEvent>{
private Model model;
private View view;
public Controller(Model model, View view)
{
this.model = model;
this.view = view;
}
public Controller()
{
initalize();
}
public Controller initalize()
{
this.model = new Model();
this.view = new View();
return this;
}
@Override
public void handle(ActionEvent event)
{
String id = ((javafx.scene.Node)event.getSource()).getId();
String conversion = view.getConversion();
switch (id) {
case "input":
if(conversion.equals("dist")) {
view.setInput2(model.kilometer(view.getInput()));
break;
}else if(conversion.equals("temp")) {
view.setInput2(model.cToF(view.getInput()));
break;
}else if(conversion.equals("weight")) {
view.setInput2(model.kilogram(view.getInput()));
break;
}
case "input2":
if(conversion.equals("dist")) {
view.setInput(model.mile(view.getInput2()));
break;
}else if(conversion.equals("temp")) {
view.setInput(model.fToC(view.getInput2()));
break;
}else if(conversion.equals("weight")) {
view.setInput(model.pound(view.getInput2()));
break;
}
default:
break;
}
}
}
主要class
package converter;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class Main extends Application {
private View view;
@Override
public void init()
{
Model model = new Model();
view = new View();
new Controller(model, view);
}
@Override
public void start(Stage primaryStage) {
try {
primaryStage.setMinWidth(350);
primaryStage.setMinHeight(150);
primaryStage.setScene(new Scene(view));
primaryStage.show();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
堆栈跟踪
Exception in Application init method
java.lang.reflect.InvocationTargetException
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:78)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:567)
at javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplicationWithArgs(LauncherImpl.java:465)
at javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplication(LauncherImpl.java:364)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:78)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:567)
at java.base/sun.launcher.LauncherHelper$FXHelper.main(LauncherHelper.java:1071)
Caused by: java.lang.RuntimeException: Exception in Application init method
at javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:896)
at javafx.graphics/com.sun.javafx.application.LauncherImpl.lambda$launchApplication(LauncherImpl.java:196)
at java.base/java.lang.Thread.run(Thread.java:831)
Caused by: java.lang.WhosebugError
at javafx.graphics/javafx.scene.Node.getScene(Node.java:1148)
at javafx.graphics/javafx.scene.Node.updateCanReceiveFocus(Node.java:8502)
at javafx.graphics/javafx.scene.Node.setTreeVisible(Node.java:8420)
at javafx.graphics/javafx.scene.Node.updateTreeVisible(Node.java:8411)
at javafx.graphics/javafx.scene.Node.<init>(Node.java:2596)
at javafx.graphics/javafx.scene.Parent.<init>(Parent.java:1418)
at javafx.graphics/javafx.scene.layout.Region.<init>(Region.java:627)
at javafx.graphics/javafx.scene.layout.Pane.<init>(Pane.java:136)
at javafx.graphics/javafx.scene.layout.BorderPane.<init>(BorderPane.java:219)
at converter.View.<init>(View.java:52)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
at converter.View.<init>(View.java:12)
Exception running application converter.Main
您正在此处创建循环依赖。控制器依赖于视图依赖于控制器。
这是糟糕的设计。
您应该实现控制反转并依赖于抽象。抽象要么在其构造中赋予视图,要么存在工厂或某些依赖注入框架。另一种选择是让您的视图发出控制器订阅的视图特定事件。有很多方法可以正确解决这个问题 - 循环依赖不是其中之一。
抽象应该是尽可能小的完全抽象(接口隔离原则),所以如果你需要一个处理程序,你可能有一个接口只有一个handle(event)
函数,没有更多。
这并不是禁止Controller实现handle函数,可能会违反单一职责原则,但这要看controller的问题和实现来决定。
在此处查看 SOLID 原则:Wikipedia SOLID
更新:您快完成了,只需将 setEventHandler 添加为视图 属性。
public void setEventHandler(@NotNull EventHandler handler) {
this.handler = handler;
updateHandler();
}
private void updateHandler() {
//Add TextField ActionEvent
input.setOnAction(handler::handle);
input2.setOnAction(handler::handle);
}
并将其添加到您的 Main::init
:
Controller controller = new Controller(model , view);
view.setEventHandler(controller);
您还必须修复控制器的默认构造函数。
您的异常发生是因为您在构造 View
实例时有无限递归:
public class View {
// ...
private Controller control = new Controller(new Model(), new View());
// ...
}
当您创建一个 View
实例时,您会尝试创建一个新的 View
实例(以传递给 Controller
构造函数),而后者又会创建一个新的 View
实例传递给 Controller
构造函数,等等。即使你在这里解决了依赖关系,你也希望这些对象引用同一个实例;您不想到处创建新实例。
MVC 有几种不同的变体。看起来您正在尝试实现“传统”MVC,其中:
- 视图观察模型,并在模型发生变化时进行更新
- 视图将用户对其封装的组件(例如文本字段)的操作委托给控制器
- 控制器更新模型
所以:
- 视图应该引用控制器和模型
- 控制器应该引用模型
- 模型应该对视图或控制器一无所知
我还建议不要让控制器实现任何 EventHandler
接口;只需定义处理用户输入所需的方法。为每个用户操作定义单独的方法,而不是使用一个单一的 handle()
方法和无穷无尽的 switch
或 if-else
语句。
所以像这样:
public class View {
private final Controller controller ;
private final Model model ;
private final TextField input ;
private final TextField input2 ;
// ...
public View(Model model, Controller controller) {
this.model = model ;
this.controller = controller ;
input = new TextField();
input2 = new TextField();
model.someProperty().addListener((obs, oldValue, newValue) -> {
/* update controls */
});
model.someOtherProperty().addListener((obs, oldValue, newValue) -> {
/* update controls */
});
input.setOnAction(event -> controller.handleInput(input.getText()));
input2.setOnAction(event -> controller.handleInput2(input2.getText()));
// layout etc
}
}
public class Controller {
public final Model model ;
public Controller(Model model) {
this.model = model ;
}
public void handleInput(String input) {
model.setSomeValue(input);
}
public void handleInput2(String input) {
model.setSomeOtherValue(input2);
}
// etc
}
然后你 assemble 使用类似
的代码Model model = new Model();
Controller controller = new Controller(model);
View view = new View(model, controller);
有关完整示例,请参阅