在JavaFX应用程序中使用POJO作为模型层 [英] Using POJOs as model layer in JavaFX application

查看:128
本文介绍了在JavaFX应用程序中使用POJO作为模型层的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在创建简单的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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆