我是新來的GUI世界/ OO設計模式,我想爲我的GUI應用程序使用MVC模式,我已經閱讀了關於MVC模式的一個小教程,模型將包含數據,視圖將包含視覺元素,控制器將在視圖和模型之間建立聯繫。對JavaFx應用MVC
我有一個視圖,包含一個ListView節點,並且ListView將填充名稱,從一個人類(模型)。但是我對一件事有點困惑。
我想知道的是,如果從文件加載數據是控制器或模型的責任??而名稱的ObservableList:它是否應該存儲在Controller或Model中?
我是新來的GUI世界/ OO設計模式,我想爲我的GUI應用程序使用MVC模式,我已經閱讀了關於MVC模式的一個小教程,模型將包含數據,視圖將包含視覺元素,控制器將在視圖和模型之間建立聯繫。對JavaFx應用MVC
我有一個視圖,包含一個ListView節點,並且ListView將填充名稱,從一個人類(模型)。但是我對一件事有點困惑。
我想知道的是,如果從文件加載數據是控制器或模型的責任??而名稱的ObservableList:它是否應該存儲在Controller或Model中?
這種模式有許多不同的變化。特別是,在Web應用程序的上下文中,「MVC」在厚客戶端(例如桌面)應用程序的上下文中與「MVC」稍有不同,因爲Web應用程序必須位於請求響應週期之上。這只是在使用JavaFX的胖客戶端應用程序環境中實現MVC的一種方法。
您的Person
類不是真正的模型,除非您有一個非常簡單的應用程序:這通常是我們所稱的域對象,並且該模型將包含對它的引用以及其他數據。在一個狹窄的範圍內,當你只是思考ListView
,你能想到的Person
爲您的數據模型(它機型在ListView
的每個元素的數據),但在應用的大背景,例如,有更多的數據和狀態需要考慮。
如果您顯示的是ListView<Person>
,您至少需要的數據是ObservableList<Person>
。您可能還需要一個屬性,例如currentPerson
,它可能代表列表中的所選項目。
如果你有唯一觀點是ListView
,然後創建一個單獨的類來存儲,這將是矯枉過正,但任何真正的應用程序通常將多個視圖結束。此時,在模型中共享數據成爲不同控制器相互通信的非常有用的方法。
因此,舉例來說,你可能有這樣的事情:
public class DataModel {
private final ObservableList<Person> personList = FXCollections.observableArrayList();
private final ObjectProperty<Person> currentPerson = new SimpleObjectPropery<>(null);
public ObjectProperty<Person> currentPersonProperty() {
return currentPerson ;
}
public final Person getCurrentPerson() {
return currentPerson().get();
}
public final void setCurrentPerson(Person person) {
currentPerson().set(person);
}
public ObservableList<Person> getPersonList() {
return personList ;
}
}
現在,你可能會爲ListView
顯示,看起來像這樣的控制器:
public class ListController {
@FXML
private ListView<Person> listView ;
private DataModel model ;
public void initModel(DataModel model) {
// ensure model is only set once:
if (this.model != null) {
throw new IllegalStateException("Model can only be initialized once");
}
this.model = model ;
listView.setItems(model.getPersonList());
listView.getSelectionModel().selectedItemProperty().addListener((obs, oldSelection, newSelection) ->
model.setCurrentPerson(newSelection));
model.currentPersonProperty().addListener((obs, oldPerson, newPerson) -> {
if (newPerson == null) {
listView.getSelectionModel().clearSelection();
} else {
listView.getSelectionModel().select(newPerson);
}
});
}
}
該控制器本質上只是結合列表中的數據顯示在模型中的數據中,並確保模型的currentPerson
始終是列表視圖中的選定項目。
現在,您可能會看到另一個視圖(編輯器),其中有一個人的firstName
,lastName
和email
屬性有三個文本字段。它的控制器看起來像:
public class EditorController {
@FXML
private TextField firstNameField ;
@FXML
private TextField lastNameField ;
@FXML
private TextField emailField ;
private DataModel model ;
public void initModel(DataModel model) {
if (this.model != null) {
throw new IllegalStateException("Model can only be initialized once");
}
this.model = model ;
model.currentPersonProperty().addListener((obs, oldPerson, newPerson) -> {
if (oldPerson != null) {
firstNameField.textProperty().unbindBidirectional(oldPerson.firstNameProperty());
lastNameField.textProperty().unbindBidirectional(oldPerson.lastNameProperty());
emailField.textProperty().unbindBidirectional(oldPerson.emailProperty());
}
if (newPerson == null) {
firstNameField.setText("");
lastNameField.setText("");
emailField.setText("");
} else {
firstNameField.textProperty().bindBidirectional(newPerson.firstNameProperty());
lastNameField.textProperty().bindBidirectional(newPerson.lastNameProperty());
emailField.textProperty().bindBidirectional(newPerson.emailProperty());
}
});
}
}
現在,如果你設置的東西,所以這兩個控制器都共享同一個模型,編輯器會在列表編輯當前選定的項目。
加載和保存數據應通過模型完成。有時甚至可以將它分解到模型具有引用的單獨類中(例如,您可以輕鬆地在基於文件的數據加載器和數據庫數據加載器或訪問Web服務的實現之間切換)。在簡單的情況下,你可能會做
public class DataModel {
// other code as before...
public void loadData(File file) throws IOException {
// load data from file and store in personList...
}
public void saveData(File file) throws IOException {
// save contents of personList to file ...
}
}
那麼你可能有一個控制器,提供了訪問此功能:
public class MenuController {
private DataModel model ;
@FXML
private MenuBar menuBar ;
public void initModel(DataModel model) {
if (this.model != null) {
throw new IllegalStateException("Model can only be initialized once");
}
this.model = model ;
}
@FXML
public void load() {
FileChooser chooser = new FileChooser();
File file = chooser.showOpenDialog(menuBar.getScene().getWindow());
if (file != null) {
try {
model.loadData(file);
} catch (IOException exc) {
// handle exception...
}
}
}
@FXML
public void save() {
// similar to load...
}
}
現在您可以輕鬆組建一個應用程序:
public class ContactApp extends Application {
@Override
public void start(Stage primaryStage) throws Exception {
BorderPane root = new BorderPane();
FXMLLoader listLoader = new FXMLLoader(getClass().getResource("list.fxml"));
root.setCenter(listLoader.load());
ListController listController = listLoader.getController();
FXMLLoader editorLoader = new FXMLLoader(getClass().getResource("editor.fxml"));
root.setRight(editorLoader.load());
EditorController editorController = editorLoader.getController();
FXMLLoader menuLoader = new FXMLLoader(getClass().getResource("menu.fxml"));
root.setTop(menuLoader.load());
MenuController menuController = menuLoader.getController();
DataModel model = new DataModel();
listController.initModel(model);
editorController.initModel(model);
menuController.initModel(model);
Scene scene = new Scene(root, 800, 600);
primaryStage.setScene(scene);
primaryStage.show();
}
}
由於我說過,這種模式有很多變化(這可能更多的是模型視圖演示者或「被動視圖」變體),但這是一種方法(我基本上贊成)。將模型通過構造函數提供給控制器會更自然一些,但是使用fx:controller
屬性來定義控制器類會困難得多。這種模式也強烈依賴於依賴注入框架。
更新:本示例的完整代碼是here。
設計模式的很好的總結,但是當你設置模型時你的代碼不會總是拋出異常,因爲if語句引用(希望非空)模型參數而不是模型實例變量?你應該使用if(this.model!= null)來代替。 – mipa
哦,很好的地方:謝謝。這就是直接在這裏輸入代碼的原因,而不是先實際運行代碼。更新與修復。 –
@James_D是否有一個原因,你不雙向綁定listView.getSelectionModel()。selectedItemProperty()到model.currentPersonProperty()?當沒有選擇任何東西時,Afaik selectedItemProperty也是null。 – findusl
我想知道的是,如果從文件加載數據是控制器或模型的責任?
對我來說,模型只負責提供表示應用程序業務邏輯的requiered數據結構。
加載來自任何源的數據的操作應該由控制器層完成。您也可以使用repository pattern,這可以幫助您在從視圖中訪問數據時從源類型中進行抽象。有了這個實現的,你不應該關心的庫implentation從文件,SQL,NoSQL的,web服務加載數據...
而且名字的ObservableList將被存儲在控制器或模型?
對我來說,ObservableList是視圖的一部分。這是可以綁定到javafx控件的那種數據結構。例如,ObservableList可以用模型中的字符串填充,但ObservableList引用應該是某些View類的屬性。 在Javafx中,使用由模型中的域對象支持的Observable屬性綁定javafx控件非常令人滿意。你可以看看viewmodel concept。對於我來說,一個由POJO支持的JavaFx bean可以被視爲一個視圖模型,您可以將它看作一個準備好在視圖中呈現的模型對象。因此,例如,如果您的視圖需要顯示從2個模型屬性計算出的總值,則此總值可能是視圖模型的一個屬性。這個屬性不會被保留下來,只要你顯示視圖就會計算出來。
這個PAQ是有見地的http://stackoverflow.com/questions/23187932/mvc-with-javafx?rq=1 – Cobusve
你可能想看看MVVM模式,它是MVC模式的變體非常適合JavaFX imho。 – findusl