在JavaFX应用程序中使用POJO作为模型层 [英] Using POJOs as model layer in JavaFX application
问题描述
我正在创建简单的JavaFX应用程序。我希望我的模型层完全独立于JavaFX - 没有 StringProperty
, IntegerProperty
等作为字段。我希望它是POJO。这样做的主要原因是我希望它是Serializable。
我创建了 DataRepository
- 简单的类CRUD接口及其一些实现,因此我可以随时更改存储数据的位置 - XML文件,SQLite数据库或其他任何东西我还必须以某种方式将我的数据存储与JavaFX连接(以在TableView中显示其内容),因此我决定创建包含我的存储库的 ObservableList
的实现。我的问题是 - 还有其他方法吗? ObservableList
包含大约30个要实现的方法,看起来我做错了。
I'm creating simple JavaFX application. I want my model layer to be completely independent from JavaFX - no StringProperty
, IntegerProperty
and etc. as fields. I want it to be POJO. Main reason to do so is that I want it to be Serializable.
I've created DataRepository
- simple CRUD-like interface and some implementations of it, so I can at anytime change where I store my data - XML file, SQLite database or anything else. I also have to somehow connect my data storage with JavaFX (to display its content in TableView), so I decided to create my implementation of ObservableList
which wraps my repository. My question is - is there any other way? ObservableList
contains about 30 methods to implement and it looks like I'm doing something wrong.
我的(简化) model:
My (simplified) model:
public class Movie implements Serializable {
private String title;
private String director;
public Movie() {
}
public Movie(String title, String director) {
this.title = title;
this.director = director;
}
// Getters and setters, equals etc...
}
MovieRepository:
MovieRepository:
public interface MovieRepository {
public void add(Movie movie);
public void remove(String title);
public void remove(int index);
public Movie get(String title);
public Movie get(int index);
public List<Movie> getAll();
}
主视图的控制器:
public class MainController {
@FXML
private TableView<Movie> movieTable;
@FXML
private TableColumn<Movie, String> movieTitleColumn;
@FXML
private Label titleLabel;
private MovieRepository movies = new DBMovieRepository(); //MovieRepository implementation which uses SQLite DB to store data
private MainApp app;
@FXML
private void initialize() {
movieTable.setItems(new ObservableMovies(movies));
// ObservableMovies is my implementation of ObservableList
// It basically wraps methods from MovieRepository
// and notifies listeners
showMovieDetails(null);
movieTitleColumn.setCellValueFactory(cellData -> new ReadOnlyStringWrapper(cellData.getValue().getTitle()));
movieTable.getSelectionModel().selectedItemProperty()
.addListener((observable, oldValue, newValue) -> showMovieDetails(newValue));
}
private void showMovieDetails(Movie movie) {
if(movie != null) {
titleLabel.setText(movie.getTitle());
} else {
titleLabel.setText("");
}
}
@FXML
private void handleNew() {
Movie movie = new Movie();
app.showNewMovieDialog(movie);
movieTable.getItems().add(movie);
}
public void setApp(MainApp app) {
this.app = app;
}
}
推荐答案
你这里有几个选项(可能更多),本网站上的其他问题也包括在内。但是,为方便起见,我也会在这里总结一下。
You have a couple of options here (maybe more), which are covered in other questions on this site. However, for convenience, I'll summarize them here too.
1。使用JavaFX属性并使类Serializable
您可以使用自定义序列化表单执行此操作。使JavaFX属性 transient
并实现 readObject
和 writeObject
到存储它们包装的值:
You can do this with a custom serialized form. Make the JavaFX properties transient
and implement readObject
and writeObject
to store the values they wrap:
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.Objects;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
public class Movie implements Serializable {
private transient StringProperty title = new SimpleStringProperty();
private transient StringProperty director = new SimpleStringProperty();
public Movie() {
}
public Movie(String title, String director) {
setTitle(title);
setDirector(director);
}
@Override
public int hashCode() {
return Objects.hash(getDirector(), getTitle());
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Movie other = (Movie) obj;
return Objects.equals(getTitle(), other.getTitle())
&& Objects.equals(getDirector(), other.getDirector());
}
public final StringProperty titleProperty() {
return this.title;
}
public final String getTitle() {
return this.titleProperty().get();
}
public final void setTitle(final String title) {
this.titleProperty().set(title);
}
public final StringProperty directorProperty() {
return this.director;
}
public final String getDirector() {
return this.directorProperty().get();
}
public final void setDirector(final String director) {
this.directorProperty().set(director);
}
private void readObject(ObjectInputStream s) throws ClassNotFoundException, IOException {
s.defaultReadObject();
title = new SimpleStringProperty((String) s.readObject());
director = new SimpleStringProperty((String) s.readObject());
}
private void writeObject(ObjectOutputStream s) throws IOException {
s.defaultWriteObject();
s.writeObject(getTitle());
s.writeObject(getDirector());
}
}
2。使用带有绑定属性的POJO。
参见 JavaBean包装与JavaFX属性以获取详细信息。简而言之:
See JavaBean wrapping with JavaFX Properties for details. In brief:
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
public class Movie {
private String title ;
private String director ;
private final PropertyChangeSupport propertySupport ;
public Movie(String title, String director) {
this.title = title ;
this.director = director ;
this.propertySupport = new PropertyChangeSupport(this);
}
public Movie() {
this("", "");
}
public String getTitle() {
return title ;
}
public String setTitle(String title) {
String oldTitle = this.title ;
this.title = title ;
propertySupport.firePropertyChange("title", oldTitle, title);
}
// similarly for director...
public void addPropertyChangeListener(PropertyChangeListener listener) {
propertySupport.addPropertyChangeListener(listener);
}
// hashCode and equals...
}
对于想要将存储库包装为可观察列表,而是使用使用可观察列表的存储库实现包装它:
For wanting to wrap your repository as an observable list, instead wrap it with a repository implementation that uses an observable list:
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
public class ObservableMovieRepository implements MovieRepository {
private final MovieRepository repository ;
private final ObservableList<Movie> movieList;
public ObservableMovieRepository(MovieRepository repository) {
this.repository = repository ;
this.movieList = FXCollections.observableArrayList(repository.getAll());
}
@Override
public void add(Movie movie) {
repository.add(movie);
movieList.add(movie);
}
@Override
public void remove(String title) {
Movie movie = get(title);
repository.remove(title);
movieList.remove(title);
}
@Override
public void remove(int index) {
repository.remove(index);
movieList.remove(index);
}
@Override
public Movie get(String title) {
return repository.get(title);
}
@Override
public Movie get(int index) {
return movieList.get(index);
}
@Override
public ObservableList<Movie> getAll() {
return movieList ;
}
}
这使用标准 ObservableList
在创建时复制现有列表的实现,实现使该列表与包装的存储库中的列表保持同步。现在你的UI代码可以做了
This uses the standard ObservableList
implementation that copies an existing list on creation, and the implementation keeps that list in sync with the list in the wrapped repository. Now your UI code can do
ObservableMovieRepository movies = new ObservableMovieRepository(new DBMovieRepository());
// ...
movieTable.setItems(movies.getAll());
使用上面的电影
课程,你会只需执行
With the Movie
class above, you would just do
movieTitleColumn.setCellValueFactory(cellData -> cellData.getValue().titleProperty());
如果您使用POJO版本,您可以这样做
If you use the POJO version you can do
movieTitleColumn.setCellValueFactory(cellData -> {
try {
return new JavaBeanStringPropertyBuilder()
.bean(cellData.getValue())
.name("title")
.build();
} catch (Exception e) { throw new RuntimeException(e); }
}
这篇关于在JavaFX应用程序中使用POJO作为模型层的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!