在JavaFX应用程序中使用POJO作为模型层

时间:2015-10-31 14:44:56

标签: java javafx javafx-8

我正在创建简单的JavaFX应用程序。我希望我的模型层完全独立于JavaFX - 没有StringPropertyIntegerProperty等作为字段。我希望它是POJO。这样做的主要原因是我希望它是Serializable。 我创建了DataRepository - 简单的类CRUD接口及其一些实现,因此我可以随时更改存储数据的位置 - XML文件,SQLite数据库或其他任何内容。我还必须以某种方式将我的数据存储与JavaFX连接(以在TableView中显示其内容),因此我决定创建包含我的存储库的ObservableList实现。我的问题是 - 还有其他方法吗? ObservableList包含大约30种要实现的方法,看起来我做错了。

我的(简化)模型:

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:

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;
    }
}

2 个答案:

答案 0 :(得分:1)

这里有几个选项(可能更多),本网站上的其他问题也包含这些选项。但是,为了方便起见,我也在这里总结一下。

<强> 1。使用JavaFX属性并使类Serializable

您可以使用自定义序列化表单执行此操作。创建JavaFX属性transient并实现readObjectwriteObject来存储它们包装的值:

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 wrapping with JavaFX Properties。简而言之:

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...
}

如果想将您的存储库包装为可观察列表,而是使用使用可观察列表的存储库实现包装它:

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代码可以执行

ObservableMovieRepository movies = new ObservableMovieRepository(new DBMovieRepository());

// ...

movieTable.setItems(movies.getAll());

使用上面的Movie课程,您只需

movieTitleColumn.setCellValueFactory(cellData -> cellData.getValue().titleProperty());

如果您使用POJO版本,则可以

movieTitleColumn.setCellValueFactory(cellData -> {
    try {
        return new JavaBeanStringPropertyBuilder()
            .bean(cellData.getValue())
            .name("title")
            .build();
    } catch (Exception e) { throw new RuntimeException(e); }
}

答案 1 :(得分:0)

这里似乎有多个问题,所以我不确定,如果我理解正确的话,但我会尝试将它分开一点。

  

我希望我的模型层完全独立于JavaFX - 没有   StringProperty,IntegerProperty等作为字段。我想要它   POJO。

您可以将自己的属性标记为transient。然后你只需要将它们包装在你的值周围,它将兼容JavaFX和Serializable。您只需将更改传播回支持属性即可。

  

我还必须以某种方式将我的数据存储与JavaFX连接(以显示   它在TableView中的内容),所以我决定创建我的实现   包含我的存储库的ObservableList。我的问题是 - 是吗?   任何其他方式?

关于此的信息非常有限,我真的不知道,为什么你需要创建自己的ObservableList实现,但为了保持POJO,你可以保持简单java.util.Collections in您的bean并提供瞬态ObservableList,您可以在创建时通过将java.util.List包装在POJO中来创建。您可以在FXCollections实用程序类中找到这些方法。

  

ObservableList包含大约30种要实现的方法,它看起来很像   就像我做错了一样。

如果确实需要实现它,您可以继承ObservableListBase