如何在多个场景中使用JavaFX菜单项

如何在多个场景中使用JavaFX菜单项,javafx,javafx-8,fxml,Javafx,Javafx 8,Fxml,我可能是想得太多了,这就是为什么我被难住了。我构建了一个包含多个场景的应用程序。每个都有文本字段和用户填写的复选框,然后将这些信息输出为文本文件。我可以毫无问题地切换场景,我可以使用一个按钮保存文件,该按钮调用每个控制器中基本相同的函数。我在根目录上有一个菜单栏,可以切换到不同的场景,加载默认值或以前的值,然后退出。我也希望有一个“另存为”选项。我可以将“另存为”指向将写入文件的控制器或模型,但我需要从当前场景获取数据。我想我需要能够访问当前控制器才能做到这一点 这是我的主应用程序 packag

我可能是想得太多了,这就是为什么我被难住了。我构建了一个包含多个场景的应用程序。每个都有文本字段和用户填写的复选框,然后将这些信息输出为文本文件。我可以毫无问题地切换场景,我可以使用一个按钮保存文件,该按钮调用每个控制器中基本相同的函数。我在根目录上有一个菜单栏,可以切换到不同的场景,加载默认值或以前的值,然后退出。我也希望有一个“另存为”选项。我可以将“另存为”指向将写入文件的控制器或模型,但我需要从当前场景获取数据。我想我需要能够访问当前控制器才能做到这一点

这是我的主应用程序

package application;
import application.controller.MainController;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
import java.io.IOException;

public class MainApp extends Application {

@Override
public void start(Stage stage) throws Exception{
    stage.setTitle("Volundr Options File Generator");
    stage.setScene(createScene(loadMainPane()));
    stage.show();
}

/**
 * Loads the main fxml layout.
 * Sets up the vista switching VistaNavigator.
 * Loads the first vista into the fxml layout.
 *
 * @return the loaded pane.
 * @throws IOException if the pane could not be loaded.
 */
private Pane loadMainPane() throws IOException {
    FXMLLoader loader = new FXMLLoader();

    Pane mainPane = loader.load(getClass().getResourceAsStream(VistaNavigator.MAIN));
    //Pane mainPane = (Pane) loader.load(getClass().getResourceAsStream(VistaNavigator.MAIN));

    MainController mainController = loader.getController();

    VistaNavigator.setMainController(mainController);
    VistaNavigator.loadVista(VistaNavigator.VISTA_0);

    return mainPane;
}

/**
 * Creates the main application scene.
 *
 * @param mainPane the main application layout.
 *
 * @return the created scene.
 */
private Scene createScene(Pane mainPane) {
    Scene scene = new Scene(mainPane);

 scene.getStylesheets().setAll(getClass().getResource("vista.css").toExternalForm());

    return scene;
}

public static void main(String[] args) {
    launch(args);
}
}
主控制器

package application.controller;
import application.VistaNavigator;
import application.models.OptionsFileDefaults;
import javafx.fxml.FXML;
import javafx.scene.Node;
import javafx.scene.layout.StackPane;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Arrays;

public class MainController{

/** Holder of a switchable vista. */
@FXML public StackPane vistaHolder;
@FXML public PermutationAnalysisController permutationAnalysisController;

@FXML
public void programExit(){System.exit(0);}
public void singleCell(){VistaNavigator.loadVista(VistaNavigator.VISTA_1);}
public void segmentAnalyzer(){VistaNavigator.loadVista(VistaNavigator.VISTA_2);}
public void permutationAnalyzer(){VistaNavigator.loadVista(VistaNavigator.VISTA_3);}
public void syntheticLethal(){VistaNavigator.loadVista(VistaNavigator.VISTA_4);}
public void loadDefaultValues() throws IOException {OptionsFileDefaults.loadDefaultValue();}

@FXML
public void saveFile() {

    //doSomething(getClass().getMethod(generateOptionsFile));
}

/**
 * Replaces the vista displayed in the vista holder with a new vista.
 *
 * @param node the vista node to be swapped in.
 */
public void setVista(Node node) {
    vistaHolder.getChildren().setAll(node);
}
}
package application.controller;
import application.models.*;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.css.PseudoClass;
import javafx.fxml.FXML;
import javafx.scene.control.*;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import org.apache.commons.lang3.StringUtils;
import java.awt.*;
import java.io.*;
import java.util.Objects;

import static application.models.OptionsDataCollector.*;
import static application.models.OptionsDataCollector.targetPermFile;
import static application.models.OptionsFileDefaults.defaultValues;
import static application.models.ValidationForm.*;

/**
 * Controller class to handle displaying the GUI showing the options required for the permutation analysis.
 * PermutationAnalysisController.java
 *
 * @author Dennis A. Simpson
 * @version 0.5beta
 * @since July 9, 2016
 */

public class PermutationAnalysisController {

private Desktop desktop = Desktop.getDesktop();
@FXML private TextField segmentFile = new TextField();
@FXML private TextField cellName = new TextField();
@FXML private TextField targetGroupSize = new TextField();
@FXML private TextField libraryName = new TextField();
@FXML private TextField spawnJobs = new TextField();
@FXML private CheckBox excludeChrY = new CheckBox();
@FXML private CheckBox writeMapFile = new CheckBox();
@FXML private CheckBox segPermFile = new CheckBox();
@FXML private CheckBox targetPermFile = new CheckBox();
@FXML private TextField targetBedFile = new TextField();
@FXML private TextField freqIterations = new TextField();
@FXML private TextField repeatCount = new TextField();
@FXML private TextField bedGroup = new TextField();
@FXML private TextField progCheck = new TextField();
@FXML private Label targetGroupSizeLabel;
@FXML private Button loadValues;

private final PseudoClass errorClass = PseudoClass.getPseudoClass("error");
private final PseudoClass hiddenClass = PseudoClass.getPseudoClass("hidden");

@FXML public void initialize(){

    OptionsDataCollector.runModule = "--Permutation_Analysis";
    OptionsDataCollector.excludeChrY = excludeChrY;
    OptionsDataCollector.writeMapFile = writeMapFile;
    OptionsDataCollector.segPermFile = segPermFile;
    OptionsDataCollector.targetPermFile = targetPermFile;

    spawnJobs.pseudoClassStateChanged(errorClass, false);
    spawnJobs.setText("1");
    OptionsDataCollector.spawnJobs = spawnJobs.getText();

    segmentFile.pseudoClassStateChanged(errorClass, true);
    cellName.pseudoClassStateChanged(errorClass, true);
    libraryName.pseudoClassStateChanged(errorClass, true);

    progCheck.pseudoClassStateChanged(errorClass, false);
    progCheck.setText("20000");
    OptionsDataCollector.progCheck = progCheck.getText();

    targetBedFile.pseudoClassStateChanged(errorClass, true);
    freqIterations.pseudoClassStateChanged(errorClass, true);
    repeatCount.pseudoClassStateChanged(errorClass, true);
    bedGroup.pseudoClassStateChanged(errorClass, true);

    targetGroupSize.pseudoClassStateChanged(hiddenClass, true);
    targetGroupSize.pseudoClassStateChanged(errorClass, true);
    targetGroupSizeLabel.pseudoClassStateChanged(hiddenClass, true);

    targetBedFile.focusedProperty().addListener((observable, oldValue, newValue) ->{
        targetBedFile.pseudoClassStateChanged(errorClass, !pathValidate(targetBedFile.getText()));
        OptionsDataCollector.targetBedFile = targetBedFile.getText();
    });

    freqIterations.focusedProperty().addListener((observable, oldValue, newValue) -> {
        if(!Objects.equals(freqIterations.getText(), "0") && numberValidate(freqIterations.getText())){
            freqIterations.pseudoClassStateChanged(errorClass, false);
            OptionsDataCollector.freqIterations = freqIterations.getText();
        }else{
            freqIterations.pseudoClassStateChanged(errorClass, true);
            OptionsDataCollector.freqIterations = freqIterations.getText();
        }
    });

    repeatCount.focusedProperty().addListener((observable, oldValue, newValue) -> {
        if(!Objects.equals(repeatCount.getText(), "0") && numberValidate(repeatCount.getText())){
            repeatCount.pseudoClassStateChanged(errorClass, false);
            OptionsDataCollector.repeatCount = repeatCount.getText();
        }else{
            repeatCount.pseudoClassStateChanged(errorClass, true);
            OptionsDataCollector.repeatCount = repeatCount.getText();
        }
    });

    progCheck.focusedProperty().addListener((observable, oldValue, newValue) -> {
        if(numberValidate(progCheck.getText())){
            progCheck.pseudoClassStateChanged(errorClass, false);
            OptionsDataCollector.progCheck = progCheck.getText();
        }else{
            progCheck.pseudoClassStateChanged(errorClass, true);
            OptionsDataCollector.progCheck = progCheck.getText();
        }
    });

    spawnJobs.focusedProperty().addListener((observable, oldValue, newValue) -> {
        if(!Objects.equals(spawnJobs.getText(), "0") && numberValidate(spawnJobs.getText())){
            spawnJobs.pseudoClassStateChanged(errorClass, false);
            OptionsDataCollector.spawnJobs = spawnJobs.getText();
        }else{
            spawnJobs.pseudoClassStateChanged(errorClass, true);
            OptionsDataCollector.spawnJobs = spawnJobs.getText();
        }
    });

    bedGroup.focusedProperty().addListener((observable, oldValue, newValue) -> {
        if(!Objects.equals(bedGroup.getText(), "0") && numberValidate(bedGroup.getText())){
            bedGroup.pseudoClassStateChanged(errorClass, false);
            OptionsDataCollector.bedGroup = bedGroup.getText();
        }else{
            bedGroup.pseudoClassStateChanged(errorClass, true);
            OptionsDataCollector.bedGroup = bedGroup.getText();
        }
    });

    segmentFile.focusedProperty().addListener((observable, oldValue, newValue) -> {
        segmentFile.pseudoClassStateChanged(errorClass, !pathValidate(segmentFile.getText()));
        OptionsDataCollector.segmentFile = segmentFile.getText();
    });

    cellName.focusedProperty().addListener((observable, oldValue, newValue) -> {
        cellName.pseudoClassStateChanged(errorClass, !(!StringUtils.isBlank(cellName.getText()) &&
                textValidate(cellName.getText())));
        OptionsDataCollector.cellName = cellName.getText();
    });

    libraryName.focusedProperty().addListener((observable, oldValue, newValue) -> {
        if(!StringUtils.isBlank(libraryName.getText()) && textValidate(libraryName.getText())){
            libraryName.pseudoClassStateChanged(errorClass, false);
            OptionsDataCollector.libraryName = libraryName.getText();
        }else{
            libraryName.pseudoClassStateChanged(errorClass, true);
            OptionsDataCollector.libraryName = libraryName.getText();
        }
    });

    targetPermFile.focusedProperty().addListener(((observable, oldValue, newValue) -> {
        if(targetPermFile.isSelected()){
            targetGroupSize.pseudoClassStateChanged(hiddenClass, false);
            OptionsDataCollector.targetGroupSize = targetGroupSize.getText();
            targetGroupSizeLabel.pseudoClassStateChanged(hiddenClass, false);
        }
        else {if (!targetPermFile.isSelected()) {
            targetGroupSize.pseudoClassStateChanged(hiddenClass, true);
            OptionsDataCollector.targetGroupSize = targetGroupSize.getText();
            targetGroupSizeLabel.pseudoClassStateChanged(hiddenClass, true);
        }}
    }));

    targetGroupSize.focusedProperty().addListener((observable, oldValue, newValue) -> {
        if(!Objects.equals(targetGroupSize.getText(), "0") && numberValidate(targetGroupSize.getText())){
            targetGroupSize.pseudoClassStateChanged(errorClass, false);
            OptionsDataCollector.targetGroupSize = targetGroupSize.getText();
        }else{
            targetGroupSize.pseudoClassStateChanged(errorClass, true);
            OptionsDataCollector.targetGroupSize = targetGroupSize.getText();
        }
    });

    excludeChrY.selectedProperty().addListener(new ChangeListener<Boolean>() {
        public void changed(ObservableValue<? extends Boolean> ov,Boolean old_val, Boolean new_val) {
            OptionsDataCollector.excludeChrY = excludeChrY;
        }
    });

    writeMapFile.selectedProperty().addListener(new ChangeListener<Boolean>() {
        public void changed(ObservableValue<? extends Boolean> ov,Boolean old_val, Boolean new_val) {
            OptionsDataCollector.writeMapFile = writeMapFile;
        }
    });

    segPermFile.selectedProperty().addListener(new ChangeListener<Boolean>() {
        public void changed(ObservableValue<? extends Boolean> ov,Boolean old_val, Boolean new_val) {
            OptionsDataCollector.segPermFile = segPermFile;
        }
    });

    targetPermFile.selectedProperty().addListener(new ChangeListener<Boolean>() {
        public void changed(ObservableValue<? extends Boolean> ov,Boolean old_val, Boolean new_val) {
            OptionsDataCollector.targetPermFile = targetPermFile;
        }
    });
}

@FXML
private void loadDefaultValues() throws IOException {
    if(OptionsFileDefaults.inputOptionsFile == null){
        Alert alert = new Alert(Alert.AlertType.ERROR);
        alert.setTitle("Error Dialog");
        alert.setHeaderText("You Must First Import The File With The Default Settings!");
        //alert.setContentText("You Must First Import The File With The Default Settings!");
        alert.showAndWait();
    }else {
        defaultValues();
        segmentFile.setText(OptionsFileDefaults.getSegmentFile());
        segmentFile.pseudoClassStateChanged(errorClass, !pathValidate(segmentFile.getText()));
        OptionsDataCollector.segmentFile = segmentFile.getText();

        targetBedFile.setText(OptionsFileDefaults.getTargetBedFile());
        targetBedFile.pseudoClassStateChanged(errorClass, !pathValidate(targetBedFile.getText()));
        OptionsDataCollector.targetBedFile = targetBedFile.getText();

        cellName.setText(OptionsFileDefaults.getCellName());
        cellName.pseudoClassStateChanged(errorClass, !(!StringUtils.isBlank(cellName.getText()) && textValidate
                (cellName.getText())));
        OptionsDataCollector.cellName = cellName.getText();

        libraryName.setText(OptionsFileDefaults.getLibraryName());
        libraryName.pseudoClassStateChanged(errorClass, !(!StringUtils.isBlank(libraryName.getText()) &&
                textValidate(libraryName.getText())));
        OptionsDataCollector.libraryName = libraryName.getText();

        spawnJobs.setText(OptionsFileDefaults.getSpawnJobs());
        spawnJobs.pseudoClassStateChanged(errorClass, !(!StringUtils.isBlank(spawnJobs.getText()) &&
                !Objects.equals(spawnJobs.getText(), 0) && numberValidate(spawnJobs.getText())));
        OptionsDataCollector.spawnJobs = spawnJobs.getText();

        freqIterations.setText(OptionsFileDefaults.getFreqIterations());
        freqIterations.pseudoClassStateChanged(errorClass, !(!StringUtils.isBlank(freqIterations.getText()) &&
                !Objects.equals(freqIterations.getText(), 0) && numberValidate(freqIterations.getText())));
        OptionsDataCollector.freqIterations = freqIterations.getText();

        repeatCount.setText(OptionsFileDefaults.getRepeatCount());
        repeatCount.pseudoClassStateChanged(errorClass, !(!StringUtils.isBlank(repeatCount.getText()) &&
                !Objects.equals(repeatCount.getText(), 0) && numberValidate(repeatCount.getText())));
        OptionsDataCollector.repeatCount = repeatCount.getText();

        bedGroup.setText(OptionsFileDefaults.getBedGroup());
        bedGroup.pseudoClassStateChanged(errorClass, !(!StringUtils.isBlank(bedGroup.getText()) &&
                !Objects.equals(bedGroup.getText(), 0) && numberValidate(bedGroup.getText())));
        OptionsDataCollector.bedGroup = bedGroup.getText();

        progCheck.setText(OptionsFileDefaults.getProgCheck());
        progCheck.pseudoClassStateChanged(errorClass, !(!StringUtils.isBlank(progCheck.getText()) &&
                numberValidate(progCheck.getText())));
        OptionsDataCollector.progCheck = progCheck.getText();

        excludeChrY.setSelected(OptionsFileDefaults.isExcludeChrY());
        OptionsDataCollector.excludeChrY = excludeChrY;

        writeMapFile.setSelected(OptionsFileDefaults.isWriteMapFile());
        OptionsDataCollector.writeMapFile = writeMapFile;

        segPermFile.setSelected(OptionsFileDefaults.isSegPermFile());
        OptionsDataCollector.segPermFile = segPermFile;

        targetPermFile.setSelected(OptionsFileDefaults.isTargetPermFile());
        if(targetPermFile.isSelected()){targetGroupSize.pseudoClassStateChanged(hiddenClass, false);}
        OptionsDataCollector.segPermFile = segPermFile;

        targetGroupSize.setText(OptionsFileDefaults.getTargetGroupSize());
        targetGroupSize.pseudoClassStateChanged(errorClass, !(!StringUtils.isBlank(targetGroupSize.getText()) &&
                !Objects.equals(targetGroupSize.getText(), 0) && numberValidate(targetGroupSize.getText())));
        OptionsDataCollector.targetGroupSize = targetGroupSize.getText();
    }
}
}
维斯塔纳维盖特

package application;
import application.controller.MainController;
import javafx.fxml.FXMLLoader;
import java.io.IOException;

public class VistaNavigator {

/**
 * Constants for fxml layouts managed by the navigator.
 */
static final String MAIN    = "views/main.fxml";
public static final String VISTA_0 = "views/entry_vista.fxml";
public static final String VISTA_1 = "views/single_cell_analysis.fxml"; // Single Cell
public static final String VISTA_2 = "views/segment_analysis.fxml";
public static final String VISTA_3 = "views/permutation_analysis.fxml";
public static final String VISTA_4 = "views/entry_vista.fxml"; // Synthetic lethal

/** The main application layout controller. */
private static MainController mainController;

/**
 * Stores the main controller for later use in navigation tasks.
 *
 * @param mainController the main application layout controller.
 */
static void setMainController(MainController mainController) {
    VistaNavigator.mainController = mainController;
}

/**
 * Loads the vista specified by the fxml file into the
 * vistaHolder pane of the main application layout.
 *
 * Previously loaded vista for the same fxml file are not cached.
 * The fxml is loaded anew and a new vista node hierarchy generated
 * every time this method is invoked.
 *
 * @param fxml the fxml file to be loaded.
 */
public static void loadVista(String fxml) {

    try {
        mainController.setVista(FXMLLoader.load(VistaNavigator.class.getResource(fxml)));
    } catch (IOException e) {
        e.printStackTrace();
    }
}
}

任何帮助都将不胜感激。

我将此作为一个可能的答案发布,但对我来说似乎很复杂。简而言之,我添加了一个类,该类始终从每个场景控制器获取更新。从主Save_As,我可以简单地调用文件编写器代码。我不接受这个作为最终答案,因为它似乎过于复杂,而且有点慢

置换控制器

package application.controller;
import application.VistaNavigator;
import application.models.OptionsFileDefaults;
import javafx.fxml.FXML;
import javafx.scene.Node;
import javafx.scene.layout.StackPane;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Arrays;

public class MainController{

/** Holder of a switchable vista. */
@FXML public StackPane vistaHolder;
@FXML public PermutationAnalysisController permutationAnalysisController;

@FXML
public void programExit(){System.exit(0);}
public void singleCell(){VistaNavigator.loadVista(VistaNavigator.VISTA_1);}
public void segmentAnalyzer(){VistaNavigator.loadVista(VistaNavigator.VISTA_2);}
public void permutationAnalyzer(){VistaNavigator.loadVista(VistaNavigator.VISTA_3);}
public void syntheticLethal(){VistaNavigator.loadVista(VistaNavigator.VISTA_4);}
public void loadDefaultValues() throws IOException {OptionsFileDefaults.loadDefaultValue();}

@FXML
public void saveFile() {

    //doSomething(getClass().getMethod(generateOptionsFile));
}

/**
 * Replaces the vista displayed in the vista holder with a new vista.
 *
 * @param node the vista node to be swapped in.
 */
public void setVista(Node node) {
    vistaHolder.getChildren().setAll(node);
}
}
package application.controller;
import application.models.*;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.css.PseudoClass;
import javafx.fxml.FXML;
import javafx.scene.control.*;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import org.apache.commons.lang3.StringUtils;
import java.awt.*;
import java.io.*;
import java.util.Objects;

import static application.models.OptionsDataCollector.*;
import static application.models.OptionsDataCollector.targetPermFile;
import static application.models.OptionsFileDefaults.defaultValues;
import static application.models.ValidationForm.*;

/**
 * Controller class to handle displaying the GUI showing the options required for the permutation analysis.
 * PermutationAnalysisController.java
 *
 * @author Dennis A. Simpson
 * @version 0.5beta
 * @since July 9, 2016
 */

public class PermutationAnalysisController {

private Desktop desktop = Desktop.getDesktop();
@FXML private TextField segmentFile = new TextField();
@FXML private TextField cellName = new TextField();
@FXML private TextField targetGroupSize = new TextField();
@FXML private TextField libraryName = new TextField();
@FXML private TextField spawnJobs = new TextField();
@FXML private CheckBox excludeChrY = new CheckBox();
@FXML private CheckBox writeMapFile = new CheckBox();
@FXML private CheckBox segPermFile = new CheckBox();
@FXML private CheckBox targetPermFile = new CheckBox();
@FXML private TextField targetBedFile = new TextField();
@FXML private TextField freqIterations = new TextField();
@FXML private TextField repeatCount = new TextField();
@FXML private TextField bedGroup = new TextField();
@FXML private TextField progCheck = new TextField();
@FXML private Label targetGroupSizeLabel;
@FXML private Button loadValues;

private final PseudoClass errorClass = PseudoClass.getPseudoClass("error");
private final PseudoClass hiddenClass = PseudoClass.getPseudoClass("hidden");

@FXML public void initialize(){

    OptionsDataCollector.runModule = "--Permutation_Analysis";
    OptionsDataCollector.excludeChrY = excludeChrY;
    OptionsDataCollector.writeMapFile = writeMapFile;
    OptionsDataCollector.segPermFile = segPermFile;
    OptionsDataCollector.targetPermFile = targetPermFile;

    spawnJobs.pseudoClassStateChanged(errorClass, false);
    spawnJobs.setText("1");
    OptionsDataCollector.spawnJobs = spawnJobs.getText();

    segmentFile.pseudoClassStateChanged(errorClass, true);
    cellName.pseudoClassStateChanged(errorClass, true);
    libraryName.pseudoClassStateChanged(errorClass, true);

    progCheck.pseudoClassStateChanged(errorClass, false);
    progCheck.setText("20000");
    OptionsDataCollector.progCheck = progCheck.getText();

    targetBedFile.pseudoClassStateChanged(errorClass, true);
    freqIterations.pseudoClassStateChanged(errorClass, true);
    repeatCount.pseudoClassStateChanged(errorClass, true);
    bedGroup.pseudoClassStateChanged(errorClass, true);

    targetGroupSize.pseudoClassStateChanged(hiddenClass, true);
    targetGroupSize.pseudoClassStateChanged(errorClass, true);
    targetGroupSizeLabel.pseudoClassStateChanged(hiddenClass, true);

    targetBedFile.focusedProperty().addListener((observable, oldValue, newValue) ->{
        targetBedFile.pseudoClassStateChanged(errorClass, !pathValidate(targetBedFile.getText()));
        OptionsDataCollector.targetBedFile = targetBedFile.getText();
    });

    freqIterations.focusedProperty().addListener((observable, oldValue, newValue) -> {
        if(!Objects.equals(freqIterations.getText(), "0") && numberValidate(freqIterations.getText())){
            freqIterations.pseudoClassStateChanged(errorClass, false);
            OptionsDataCollector.freqIterations = freqIterations.getText();
        }else{
            freqIterations.pseudoClassStateChanged(errorClass, true);
            OptionsDataCollector.freqIterations = freqIterations.getText();
        }
    });

    repeatCount.focusedProperty().addListener((observable, oldValue, newValue) -> {
        if(!Objects.equals(repeatCount.getText(), "0") && numberValidate(repeatCount.getText())){
            repeatCount.pseudoClassStateChanged(errorClass, false);
            OptionsDataCollector.repeatCount = repeatCount.getText();
        }else{
            repeatCount.pseudoClassStateChanged(errorClass, true);
            OptionsDataCollector.repeatCount = repeatCount.getText();
        }
    });

    progCheck.focusedProperty().addListener((observable, oldValue, newValue) -> {
        if(numberValidate(progCheck.getText())){
            progCheck.pseudoClassStateChanged(errorClass, false);
            OptionsDataCollector.progCheck = progCheck.getText();
        }else{
            progCheck.pseudoClassStateChanged(errorClass, true);
            OptionsDataCollector.progCheck = progCheck.getText();
        }
    });

    spawnJobs.focusedProperty().addListener((observable, oldValue, newValue) -> {
        if(!Objects.equals(spawnJobs.getText(), "0") && numberValidate(spawnJobs.getText())){
            spawnJobs.pseudoClassStateChanged(errorClass, false);
            OptionsDataCollector.spawnJobs = spawnJobs.getText();
        }else{
            spawnJobs.pseudoClassStateChanged(errorClass, true);
            OptionsDataCollector.spawnJobs = spawnJobs.getText();
        }
    });

    bedGroup.focusedProperty().addListener((observable, oldValue, newValue) -> {
        if(!Objects.equals(bedGroup.getText(), "0") && numberValidate(bedGroup.getText())){
            bedGroup.pseudoClassStateChanged(errorClass, false);
            OptionsDataCollector.bedGroup = bedGroup.getText();
        }else{
            bedGroup.pseudoClassStateChanged(errorClass, true);
            OptionsDataCollector.bedGroup = bedGroup.getText();
        }
    });

    segmentFile.focusedProperty().addListener((observable, oldValue, newValue) -> {
        segmentFile.pseudoClassStateChanged(errorClass, !pathValidate(segmentFile.getText()));
        OptionsDataCollector.segmentFile = segmentFile.getText();
    });

    cellName.focusedProperty().addListener((observable, oldValue, newValue) -> {
        cellName.pseudoClassStateChanged(errorClass, !(!StringUtils.isBlank(cellName.getText()) &&
                textValidate(cellName.getText())));
        OptionsDataCollector.cellName = cellName.getText();
    });

    libraryName.focusedProperty().addListener((observable, oldValue, newValue) -> {
        if(!StringUtils.isBlank(libraryName.getText()) && textValidate(libraryName.getText())){
            libraryName.pseudoClassStateChanged(errorClass, false);
            OptionsDataCollector.libraryName = libraryName.getText();
        }else{
            libraryName.pseudoClassStateChanged(errorClass, true);
            OptionsDataCollector.libraryName = libraryName.getText();
        }
    });

    targetPermFile.focusedProperty().addListener(((observable, oldValue, newValue) -> {
        if(targetPermFile.isSelected()){
            targetGroupSize.pseudoClassStateChanged(hiddenClass, false);
            OptionsDataCollector.targetGroupSize = targetGroupSize.getText();
            targetGroupSizeLabel.pseudoClassStateChanged(hiddenClass, false);
        }
        else {if (!targetPermFile.isSelected()) {
            targetGroupSize.pseudoClassStateChanged(hiddenClass, true);
            OptionsDataCollector.targetGroupSize = targetGroupSize.getText();
            targetGroupSizeLabel.pseudoClassStateChanged(hiddenClass, true);
        }}
    }));

    targetGroupSize.focusedProperty().addListener((observable, oldValue, newValue) -> {
        if(!Objects.equals(targetGroupSize.getText(), "0") && numberValidate(targetGroupSize.getText())){
            targetGroupSize.pseudoClassStateChanged(errorClass, false);
            OptionsDataCollector.targetGroupSize = targetGroupSize.getText();
        }else{
            targetGroupSize.pseudoClassStateChanged(errorClass, true);
            OptionsDataCollector.targetGroupSize = targetGroupSize.getText();
        }
    });

    excludeChrY.selectedProperty().addListener(new ChangeListener<Boolean>() {
        public void changed(ObservableValue<? extends Boolean> ov,Boolean old_val, Boolean new_val) {
            OptionsDataCollector.excludeChrY = excludeChrY;
        }
    });

    writeMapFile.selectedProperty().addListener(new ChangeListener<Boolean>() {
        public void changed(ObservableValue<? extends Boolean> ov,Boolean old_val, Boolean new_val) {
            OptionsDataCollector.writeMapFile = writeMapFile;
        }
    });

    segPermFile.selectedProperty().addListener(new ChangeListener<Boolean>() {
        public void changed(ObservableValue<? extends Boolean> ov,Boolean old_val, Boolean new_val) {
            OptionsDataCollector.segPermFile = segPermFile;
        }
    });

    targetPermFile.selectedProperty().addListener(new ChangeListener<Boolean>() {
        public void changed(ObservableValue<? extends Boolean> ov,Boolean old_val, Boolean new_val) {
            OptionsDataCollector.targetPermFile = targetPermFile;
        }
    });
}

@FXML
private void loadDefaultValues() throws IOException {
    if(OptionsFileDefaults.inputOptionsFile == null){
        Alert alert = new Alert(Alert.AlertType.ERROR);
        alert.setTitle("Error Dialog");
        alert.setHeaderText("You Must First Import The File With The Default Settings!");
        //alert.setContentText("You Must First Import The File With The Default Settings!");
        alert.showAndWait();
    }else {
        defaultValues();
        segmentFile.setText(OptionsFileDefaults.getSegmentFile());
        segmentFile.pseudoClassStateChanged(errorClass, !pathValidate(segmentFile.getText()));
        OptionsDataCollector.segmentFile = segmentFile.getText();

        targetBedFile.setText(OptionsFileDefaults.getTargetBedFile());
        targetBedFile.pseudoClassStateChanged(errorClass, !pathValidate(targetBedFile.getText()));
        OptionsDataCollector.targetBedFile = targetBedFile.getText();

        cellName.setText(OptionsFileDefaults.getCellName());
        cellName.pseudoClassStateChanged(errorClass, !(!StringUtils.isBlank(cellName.getText()) && textValidate
                (cellName.getText())));
        OptionsDataCollector.cellName = cellName.getText();

        libraryName.setText(OptionsFileDefaults.getLibraryName());
        libraryName.pseudoClassStateChanged(errorClass, !(!StringUtils.isBlank(libraryName.getText()) &&
                textValidate(libraryName.getText())));
        OptionsDataCollector.libraryName = libraryName.getText();

        spawnJobs.setText(OptionsFileDefaults.getSpawnJobs());
        spawnJobs.pseudoClassStateChanged(errorClass, !(!StringUtils.isBlank(spawnJobs.getText()) &&
                !Objects.equals(spawnJobs.getText(), 0) && numberValidate(spawnJobs.getText())));
        OptionsDataCollector.spawnJobs = spawnJobs.getText();

        freqIterations.setText(OptionsFileDefaults.getFreqIterations());
        freqIterations.pseudoClassStateChanged(errorClass, !(!StringUtils.isBlank(freqIterations.getText()) &&
                !Objects.equals(freqIterations.getText(), 0) && numberValidate(freqIterations.getText())));
        OptionsDataCollector.freqIterations = freqIterations.getText();

        repeatCount.setText(OptionsFileDefaults.getRepeatCount());
        repeatCount.pseudoClassStateChanged(errorClass, !(!StringUtils.isBlank(repeatCount.getText()) &&
                !Objects.equals(repeatCount.getText(), 0) && numberValidate(repeatCount.getText())));
        OptionsDataCollector.repeatCount = repeatCount.getText();

        bedGroup.setText(OptionsFileDefaults.getBedGroup());
        bedGroup.pseudoClassStateChanged(errorClass, !(!StringUtils.isBlank(bedGroup.getText()) &&
                !Objects.equals(bedGroup.getText(), 0) && numberValidate(bedGroup.getText())));
        OptionsDataCollector.bedGroup = bedGroup.getText();

        progCheck.setText(OptionsFileDefaults.getProgCheck());
        progCheck.pseudoClassStateChanged(errorClass, !(!StringUtils.isBlank(progCheck.getText()) &&
                numberValidate(progCheck.getText())));
        OptionsDataCollector.progCheck = progCheck.getText();

        excludeChrY.setSelected(OptionsFileDefaults.isExcludeChrY());
        OptionsDataCollector.excludeChrY = excludeChrY;

        writeMapFile.setSelected(OptionsFileDefaults.isWriteMapFile());
        OptionsDataCollector.writeMapFile = writeMapFile;

        segPermFile.setSelected(OptionsFileDefaults.isSegPermFile());
        OptionsDataCollector.segPermFile = segPermFile;

        targetPermFile.setSelected(OptionsFileDefaults.isTargetPermFile());
        if(targetPermFile.isSelected()){targetGroupSize.pseudoClassStateChanged(hiddenClass, false);}
        OptionsDataCollector.segPermFile = segPermFile;

        targetGroupSize.setText(OptionsFileDefaults.getTargetGroupSize());
        targetGroupSize.pseudoClassStateChanged(errorClass, !(!StringUtils.isBlank(targetGroupSize.getText()) &&
                !Objects.equals(targetGroupSize.getText(), 0) && numberValidate(targetGroupSize.getText())));
        OptionsDataCollector.targetGroupSize = targetGroupSize.getText();
    }
}
}

有没有人想过如何清理这个问题,让它更有效率?

我将此作为一个可能的答案发布,但对我来说似乎很复杂。简而言之,我添加了一个类,该类始终从每个场景控制器获取更新。从主Save_As,我可以简单地调用文件编写器代码。我不接受这个作为最终答案,因为它似乎过于复杂,而且有点慢

置换控制器

package application.controller;
import application.VistaNavigator;
import application.models.OptionsFileDefaults;
import javafx.fxml.FXML;
import javafx.scene.Node;
import javafx.scene.layout.StackPane;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Arrays;

public class MainController{

/** Holder of a switchable vista. */
@FXML public StackPane vistaHolder;
@FXML public PermutationAnalysisController permutationAnalysisController;

@FXML
public void programExit(){System.exit(0);}
public void singleCell(){VistaNavigator.loadVista(VistaNavigator.VISTA_1);}
public void segmentAnalyzer(){VistaNavigator.loadVista(VistaNavigator.VISTA_2);}
public void permutationAnalyzer(){VistaNavigator.loadVista(VistaNavigator.VISTA_3);}
public void syntheticLethal(){VistaNavigator.loadVista(VistaNavigator.VISTA_4);}
public void loadDefaultValues() throws IOException {OptionsFileDefaults.loadDefaultValue();}

@FXML
public void saveFile() {

    //doSomething(getClass().getMethod(generateOptionsFile));
}

/**
 * Replaces the vista displayed in the vista holder with a new vista.
 *
 * @param node the vista node to be swapped in.
 */
public void setVista(Node node) {
    vistaHolder.getChildren().setAll(node);
}
}
package application.controller;
import application.models.*;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.css.PseudoClass;
import javafx.fxml.FXML;
import javafx.scene.control.*;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import org.apache.commons.lang3.StringUtils;
import java.awt.*;
import java.io.*;
import java.util.Objects;

import static application.models.OptionsDataCollector.*;
import static application.models.OptionsDataCollector.targetPermFile;
import static application.models.OptionsFileDefaults.defaultValues;
import static application.models.ValidationForm.*;

/**
 * Controller class to handle displaying the GUI showing the options required for the permutation analysis.
 * PermutationAnalysisController.java
 *
 * @author Dennis A. Simpson
 * @version 0.5beta
 * @since July 9, 2016
 */

public class PermutationAnalysisController {

private Desktop desktop = Desktop.getDesktop();
@FXML private TextField segmentFile = new TextField();
@FXML private TextField cellName = new TextField();
@FXML private TextField targetGroupSize = new TextField();
@FXML private TextField libraryName = new TextField();
@FXML private TextField spawnJobs = new TextField();
@FXML private CheckBox excludeChrY = new CheckBox();
@FXML private CheckBox writeMapFile = new CheckBox();
@FXML private CheckBox segPermFile = new CheckBox();
@FXML private CheckBox targetPermFile = new CheckBox();
@FXML private TextField targetBedFile = new TextField();
@FXML private TextField freqIterations = new TextField();
@FXML private TextField repeatCount = new TextField();
@FXML private TextField bedGroup = new TextField();
@FXML private TextField progCheck = new TextField();
@FXML private Label targetGroupSizeLabel;
@FXML private Button loadValues;

private final PseudoClass errorClass = PseudoClass.getPseudoClass("error");
private final PseudoClass hiddenClass = PseudoClass.getPseudoClass("hidden");

@FXML public void initialize(){

    OptionsDataCollector.runModule = "--Permutation_Analysis";
    OptionsDataCollector.excludeChrY = excludeChrY;
    OptionsDataCollector.writeMapFile = writeMapFile;
    OptionsDataCollector.segPermFile = segPermFile;
    OptionsDataCollector.targetPermFile = targetPermFile;

    spawnJobs.pseudoClassStateChanged(errorClass, false);
    spawnJobs.setText("1");
    OptionsDataCollector.spawnJobs = spawnJobs.getText();

    segmentFile.pseudoClassStateChanged(errorClass, true);
    cellName.pseudoClassStateChanged(errorClass, true);
    libraryName.pseudoClassStateChanged(errorClass, true);

    progCheck.pseudoClassStateChanged(errorClass, false);
    progCheck.setText("20000");
    OptionsDataCollector.progCheck = progCheck.getText();

    targetBedFile.pseudoClassStateChanged(errorClass, true);
    freqIterations.pseudoClassStateChanged(errorClass, true);
    repeatCount.pseudoClassStateChanged(errorClass, true);
    bedGroup.pseudoClassStateChanged(errorClass, true);

    targetGroupSize.pseudoClassStateChanged(hiddenClass, true);
    targetGroupSize.pseudoClassStateChanged(errorClass, true);
    targetGroupSizeLabel.pseudoClassStateChanged(hiddenClass, true);

    targetBedFile.focusedProperty().addListener((observable, oldValue, newValue) ->{
        targetBedFile.pseudoClassStateChanged(errorClass, !pathValidate(targetBedFile.getText()));
        OptionsDataCollector.targetBedFile = targetBedFile.getText();
    });

    freqIterations.focusedProperty().addListener((observable, oldValue, newValue) -> {
        if(!Objects.equals(freqIterations.getText(), "0") && numberValidate(freqIterations.getText())){
            freqIterations.pseudoClassStateChanged(errorClass, false);
            OptionsDataCollector.freqIterations = freqIterations.getText();
        }else{
            freqIterations.pseudoClassStateChanged(errorClass, true);
            OptionsDataCollector.freqIterations = freqIterations.getText();
        }
    });

    repeatCount.focusedProperty().addListener((observable, oldValue, newValue) -> {
        if(!Objects.equals(repeatCount.getText(), "0") && numberValidate(repeatCount.getText())){
            repeatCount.pseudoClassStateChanged(errorClass, false);
            OptionsDataCollector.repeatCount = repeatCount.getText();
        }else{
            repeatCount.pseudoClassStateChanged(errorClass, true);
            OptionsDataCollector.repeatCount = repeatCount.getText();
        }
    });

    progCheck.focusedProperty().addListener((observable, oldValue, newValue) -> {
        if(numberValidate(progCheck.getText())){
            progCheck.pseudoClassStateChanged(errorClass, false);
            OptionsDataCollector.progCheck = progCheck.getText();
        }else{
            progCheck.pseudoClassStateChanged(errorClass, true);
            OptionsDataCollector.progCheck = progCheck.getText();
        }
    });

    spawnJobs.focusedProperty().addListener((observable, oldValue, newValue) -> {
        if(!Objects.equals(spawnJobs.getText(), "0") && numberValidate(spawnJobs.getText())){
            spawnJobs.pseudoClassStateChanged(errorClass, false);
            OptionsDataCollector.spawnJobs = spawnJobs.getText();
        }else{
            spawnJobs.pseudoClassStateChanged(errorClass, true);
            OptionsDataCollector.spawnJobs = spawnJobs.getText();
        }
    });

    bedGroup.focusedProperty().addListener((observable, oldValue, newValue) -> {
        if(!Objects.equals(bedGroup.getText(), "0") && numberValidate(bedGroup.getText())){
            bedGroup.pseudoClassStateChanged(errorClass, false);
            OptionsDataCollector.bedGroup = bedGroup.getText();
        }else{
            bedGroup.pseudoClassStateChanged(errorClass, true);
            OptionsDataCollector.bedGroup = bedGroup.getText();
        }
    });

    segmentFile.focusedProperty().addListener((observable, oldValue, newValue) -> {
        segmentFile.pseudoClassStateChanged(errorClass, !pathValidate(segmentFile.getText()));
        OptionsDataCollector.segmentFile = segmentFile.getText();
    });

    cellName.focusedProperty().addListener((observable, oldValue, newValue) -> {
        cellName.pseudoClassStateChanged(errorClass, !(!StringUtils.isBlank(cellName.getText()) &&
                textValidate(cellName.getText())));
        OptionsDataCollector.cellName = cellName.getText();
    });

    libraryName.focusedProperty().addListener((observable, oldValue, newValue) -> {
        if(!StringUtils.isBlank(libraryName.getText()) && textValidate(libraryName.getText())){
            libraryName.pseudoClassStateChanged(errorClass, false);
            OptionsDataCollector.libraryName = libraryName.getText();
        }else{
            libraryName.pseudoClassStateChanged(errorClass, true);
            OptionsDataCollector.libraryName = libraryName.getText();
        }
    });

    targetPermFile.focusedProperty().addListener(((observable, oldValue, newValue) -> {
        if(targetPermFile.isSelected()){
            targetGroupSize.pseudoClassStateChanged(hiddenClass, false);
            OptionsDataCollector.targetGroupSize = targetGroupSize.getText();
            targetGroupSizeLabel.pseudoClassStateChanged(hiddenClass, false);
        }
        else {if (!targetPermFile.isSelected()) {
            targetGroupSize.pseudoClassStateChanged(hiddenClass, true);
            OptionsDataCollector.targetGroupSize = targetGroupSize.getText();
            targetGroupSizeLabel.pseudoClassStateChanged(hiddenClass, true);
        }}
    }));

    targetGroupSize.focusedProperty().addListener((observable, oldValue, newValue) -> {
        if(!Objects.equals(targetGroupSize.getText(), "0") && numberValidate(targetGroupSize.getText())){
            targetGroupSize.pseudoClassStateChanged(errorClass, false);
            OptionsDataCollector.targetGroupSize = targetGroupSize.getText();
        }else{
            targetGroupSize.pseudoClassStateChanged(errorClass, true);
            OptionsDataCollector.targetGroupSize = targetGroupSize.getText();
        }
    });

    excludeChrY.selectedProperty().addListener(new ChangeListener<Boolean>() {
        public void changed(ObservableValue<? extends Boolean> ov,Boolean old_val, Boolean new_val) {
            OptionsDataCollector.excludeChrY = excludeChrY;
        }
    });

    writeMapFile.selectedProperty().addListener(new ChangeListener<Boolean>() {
        public void changed(ObservableValue<? extends Boolean> ov,Boolean old_val, Boolean new_val) {
            OptionsDataCollector.writeMapFile = writeMapFile;
        }
    });

    segPermFile.selectedProperty().addListener(new ChangeListener<Boolean>() {
        public void changed(ObservableValue<? extends Boolean> ov,Boolean old_val, Boolean new_val) {
            OptionsDataCollector.segPermFile = segPermFile;
        }
    });

    targetPermFile.selectedProperty().addListener(new ChangeListener<Boolean>() {
        public void changed(ObservableValue<? extends Boolean> ov,Boolean old_val, Boolean new_val) {
            OptionsDataCollector.targetPermFile = targetPermFile;
        }
    });
}

@FXML
private void loadDefaultValues() throws IOException {
    if(OptionsFileDefaults.inputOptionsFile == null){
        Alert alert = new Alert(Alert.AlertType.ERROR);
        alert.setTitle("Error Dialog");
        alert.setHeaderText("You Must First Import The File With The Default Settings!");
        //alert.setContentText("You Must First Import The File With The Default Settings!");
        alert.showAndWait();
    }else {
        defaultValues();
        segmentFile.setText(OptionsFileDefaults.getSegmentFile());
        segmentFile.pseudoClassStateChanged(errorClass, !pathValidate(segmentFile.getText()));
        OptionsDataCollector.segmentFile = segmentFile.getText();

        targetBedFile.setText(OptionsFileDefaults.getTargetBedFile());
        targetBedFile.pseudoClassStateChanged(errorClass, !pathValidate(targetBedFile.getText()));
        OptionsDataCollector.targetBedFile = targetBedFile.getText();

        cellName.setText(OptionsFileDefaults.getCellName());
        cellName.pseudoClassStateChanged(errorClass, !(!StringUtils.isBlank(cellName.getText()) && textValidate
                (cellName.getText())));
        OptionsDataCollector.cellName = cellName.getText();

        libraryName.setText(OptionsFileDefaults.getLibraryName());
        libraryName.pseudoClassStateChanged(errorClass, !(!StringUtils.isBlank(libraryName.getText()) &&
                textValidate(libraryName.getText())));
        OptionsDataCollector.libraryName = libraryName.getText();

        spawnJobs.setText(OptionsFileDefaults.getSpawnJobs());
        spawnJobs.pseudoClassStateChanged(errorClass, !(!StringUtils.isBlank(spawnJobs.getText()) &&
                !Objects.equals(spawnJobs.getText(), 0) && numberValidate(spawnJobs.getText())));
        OptionsDataCollector.spawnJobs = spawnJobs.getText();

        freqIterations.setText(OptionsFileDefaults.getFreqIterations());
        freqIterations.pseudoClassStateChanged(errorClass, !(!StringUtils.isBlank(freqIterations.getText()) &&
                !Objects.equals(freqIterations.getText(), 0) && numberValidate(freqIterations.getText())));
        OptionsDataCollector.freqIterations = freqIterations.getText();

        repeatCount.setText(OptionsFileDefaults.getRepeatCount());
        repeatCount.pseudoClassStateChanged(errorClass, !(!StringUtils.isBlank(repeatCount.getText()) &&
                !Objects.equals(repeatCount.getText(), 0) && numberValidate(repeatCount.getText())));
        OptionsDataCollector.repeatCount = repeatCount.getText();

        bedGroup.setText(OptionsFileDefaults.getBedGroup());
        bedGroup.pseudoClassStateChanged(errorClass, !(!StringUtils.isBlank(bedGroup.getText()) &&
                !Objects.equals(bedGroup.getText(), 0) && numberValidate(bedGroup.getText())));
        OptionsDataCollector.bedGroup = bedGroup.getText();

        progCheck.setText(OptionsFileDefaults.getProgCheck());
        progCheck.pseudoClassStateChanged(errorClass, !(!StringUtils.isBlank(progCheck.getText()) &&
                numberValidate(progCheck.getText())));
        OptionsDataCollector.progCheck = progCheck.getText();

        excludeChrY.setSelected(OptionsFileDefaults.isExcludeChrY());
        OptionsDataCollector.excludeChrY = excludeChrY;

        writeMapFile.setSelected(OptionsFileDefaults.isWriteMapFile());
        OptionsDataCollector.writeMapFile = writeMapFile;

        segPermFile.setSelected(OptionsFileDefaults.isSegPermFile());
        OptionsDataCollector.segPermFile = segPermFile;

        targetPermFile.setSelected(OptionsFileDefaults.isTargetPermFile());
        if(targetPermFile.isSelected()){targetGroupSize.pseudoClassStateChanged(hiddenClass, false);}
        OptionsDataCollector.segPermFile = segPermFile;

        targetGroupSize.setText(OptionsFileDefaults.getTargetGroupSize());
        targetGroupSize.pseudoClassStateChanged(errorClass, !(!StringUtils.isBlank(targetGroupSize.getText()) &&
                !Objects.equals(targetGroupSize.getText(), 0) && numberValidate(targetGroupSize.getText())));
        OptionsDataCollector.targetGroupSize = targetGroupSize.getText();
    }
}
}

有没有人想过如何清理并提高效率?

为什么不在
VistaNavigator
中保留对当前控制器的引用(这实际上是一个视图模型)?然后,您可以轻松检索当前控制器等。您可以使这些控制器都实现某种接口,该接口具有允许访问数据的方法(或从这些控制器执行保存操作所需的任何功能)。我最初使用了该接口,但最终得到了我所认为的,是不必要的代码复制。它复制了什么?为什么不在
VistaNavigator
中保留对当前控制器的引用(这实际上是一个视图模型)?然后,您可以轻松检索当前控制器等。您可以使这些控制器都实现某种接口,该接口具有允许访问数据的方法(或从这些控制器执行保存操作所需的任何功能)。我最初使用了该接口,但最终得到了我所认为的,是不必要的代码复制。它复制了什么?