3

我试图了解如何在我的 Java 应用程序中实现 MVC,阅读一些教程和 MVC hello worlds,但我仍然不确定很多事情,所以如果你能帮助我理解这一切,我会很高兴。

假设我有一个简单的 GUI 应用程序,用于存储和处理各种建筑物。我可以通过从列表中选择类型并按下按钮来添加建筑物。这些建筑物将存储在一些数组列表中,显示在 GUI 中并且可以编辑(房间数,楼层数......不重要)。建筑物将显示在 JComboBox 中,选择某个建筑物后,将出现该建筑物的设置面板。

到目前为止,我有 2 个“组件”。建筑物(集装箱)和建筑物。我创建了BuildingsModel类,它包含建筑物,有一些方法可以使用它们并在更改后通知观察者。然后我有一个BuildingsView类,它正在观察 BuildingsModel。然后我有BuldingsController类,它带有构造函数方法,它将 BuildingsModel 和 BuildingsView 作为参数,将视图绑定到模型作为观察者,创建一些初始建筑物并向视图添加一些侦听器。

现在我不知道如何继续。有几件事情我不是很高兴。

  1. 我已将侦听器绑定到按钮,它将从视图中的 JList 中获取当前选择,创建新对象(xxxBuildingModel)并将其添加到 BuildingsModel。然而,JList 只包含所有建筑类型的字符串表示,为了避免长 if-else 语句并为该字符串找到正确的类,我不得不使用反射。(每种建筑类型都有自己的扩展BuildingModel的类。)有没有更好的方法呢?

  2. 第二个侦听器绑定到包含已创建的构建实例的 JComboBox。在该组合框中选择建筑物后,我想显示该建筑物的设置表单。所以我想应该有一些与 BuildingModel 关联的视图类,它将显示其当前设置(模型的状态)。但我不确定,如何从 BuildingsController 的构造函数上下文中做到这一点。我只能访问建筑物的模型,那么我应该如何“找到”该实例的正确视图并显示它?也许我做错了,组合框不应该只包含模型,而是控制器(可以访问模型和视图),在这种情况下,我可以调用控制器的视图方法,该方法将从模型中获取所需的数据,传递它以查看和显示它。我不

这是代码中最重要的部分。我没有包含 BuildingModel 类及其子类,因为现在它们只是带有 toString() 方法的空白类。

    public class BuildingsController {
        public BuildingsController(final BuildingsModel model, final BuildingsView view) {
            class addBuildingButtonActionListener implements ActionListener {
                @Override
                public void actionPerformed(ActionEvent e) {
                    String selectedBuildingType;
                    if ((selectedBuildingType = view.getSelectedBuildingType()) != null) {
                        try {
                            BuildingModel newBuilding = (BuildingModel) Class.forName("building.models." + selectedBuildingType + "BuildingModel").newInstance();
                            model.addBuilding(newBuilding);

                        } catch (InstantiationException ex) {
                            //addToErrorLog
                        } catch (IllegalAccessException ex) {
                            //addToErrorLog
                        } catch (ClassNotFoundException ex) {
                            //addToErrorLog
                        }
                    } else {
                        //addToLog - NO_BUILDING_SELECTED
                    }
                }
            }

            class buildingComboBoxSelectListener implements ActionListener {
                @Override
                public void actionPerformed(ActionEvent e) {
                    BuildingModel selectedBuilding;
                    if ((selectedBuilding = view.getSelectedBuilding()) != null) {
                        //display current building form???
                    }
                }
            }
            model.addObserver(view);
            model.addBuilding(new HospitalBuildingModel());
            model.addBuilding(new SchoolBuildingModel());

            view.fillBuildingTypesList(BuildingsModel.getAllBuildingsTypes());
            view.addAddBuildingButtonListener(new addBuildingButtonActionListener());
            view.addActiveBuildingsListener(new buildingComboBoxSelectListener());
        }
    }
    public class BuildingsModel extends Observable {
        private static String[] allBuildingsTypes = {"School", "Hospital", "Stadion"};
        private ArrayList<BuildingModel> buildings = new ArrayList<BuildingModel>();

        public void addBuilding(BuildingModel building){
            this.buildings.add(building);
            this.setChanged();
            this.notifyObservers();
        }
        public BuildingModel[] getAllBuildings(){
            return this.buildings.toArray(new BuildingModel[this.buildings.size()]);
        }
        public static String[] getAllBuildingsTypes(){
            return BuildingsModel.allBuildingsTypes;
        }

    }
    public class BuildingsView implements Observer {
        private static String name = "Buldings";
        private static String addBuildingButtonText = "Add building";
        private JComboBox allActiveBuildings = new JComboBox();
        private JList buildingTypesList = new JList();
        private JButton addBuildingButton = new JButton(BuildingsView.addBuildingButtonText);


        @Override
        public void update(Observable model, Object o){
            BuildingModel[] allBuildings = ((BuildingsModel) model).getAllBuildings();
            this.allActiveBuildings.removeAllItems();
            for(BuildingModel building : allBuildings){
                this.allActiveBuildings.addItem(building);
            }
        }
        public String getSelectedBuildingType(){
            return (String) this.buildingTypesList.getSelectedValue();
        }
        public BuildingModel getSelectedBuilding(){
            return (BuildingModel) this.allActiveBuildings.getSelectedItem();
        }

        public void fillBuildingTypesList(String[] buildingTypes){
            this.buildingTypesList.setListData(buildingTypes);
        }

        public void addAddBuildingButtonListener(ActionListener l){
            this.addBuildingButton.addActionListener(l);
        }
        public void addActiveBuildingsListener(ActionListener l){
            this.allActiveBuildings.addActionListener(l);
        }
        public JComponent display(){
            JPanel panel = new JPanel();
            Border border = BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder(BuildingsView.name), BorderFactory.createEmptyBorder(5, 5, 5, 5));
            panel.setBorder(border);
            panel.add(this.allActiveBuildings);
            panel.add(this.buildingTypesList);
            panel.add(this.addBuildingButton);

            panel.setPreferredSize(new Dimension(200, 262));

            return panel;
        }
    }

主要的:

    public static void main(String[] args) {
        BuildingsView buildingsView = new BuildingsView();
        BuildingsModel buildingsModel = new BuildingsModel();
        BuildingsController buldingsController = new BuildingsController(buildingsModel, buildingsView);


        mainWindow window = new mainWindow("MVC test", buildingsView);
        window.generateDefaultLayout();
        window.showMainWindow();

    }

窗户:

public class mainWindow extends JFrame {
    private JPanel buildingsPanel = new JPanel();
    private BuildingsView buildingsView;

    public mainWindow(String title, BuildingsView buildingsView) {
        this.buildingsView = buildingsView;
        this.setTitle(title);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }
    public void generateDefaultLayout(){
        this.setLayout(new FlowLayout());
        this.setPreferredSize(new Dimension(1200, 920));
        this.addBuildingsPanel();
    }
    public void addBuildingsPanel(){
        this.buildingsPanel.add(this.buildingsView.display());
        this.add(this.buildingsPanel);
    }
    public void showMainWindow(){
        this.pack();
        this.setVisible(true);
    }
}

谢谢 :)

4

1 回答 1

4

我已将侦听器绑定到按钮,它将从视图中的 JList 中获取当前选择,创建新对象(xxxBuildingModel)并将其添加到 BuildingsModel。然而,JList 只包含所有建筑类型的字符串表示,为了避免长的 if-else 语句并为该字符串找到正确的类,我不得不使用反射。(每种建筑类型都有自己的扩展 BuildingModel 的类。)有没有更好的方法呢?

是的。不要让您的 JList 保存字符串,而是让它保存建筑(非 GUI)对象。使用 ListCellRenderer 告诉 JList 如何最好地显示每个建筑物。然后,当用户从 JList 中选择一个项目时,他们选择的是一个实际的建筑对象,而不是它的字符串表示形式。当然,不要考虑对此进行反思。

第二个侦听器绑定到包含已创建的构建实例的 JComboBox。在该组合框中选择建筑物后,我想显示该建筑物的设置表单。所以我想应该有一些与 BuildingModel 关联的视图类,它将显示其当前设置(模型的状态)。但我不确定,如何从 BuildingsController 的构造函数上下文中做到这一点。

您可以在 Control 类中有一个 AbstractAction 类,并将其用作 JComboBox 的侦听器(有关内容的更多信息,请参见我对上一个问题的回答)。

我只能访问建筑物的模型,那么我应该如何“找到”该实例的正确视图并显示它?

您将从侦听器中获取选定的建筑物,然后侦听器将通过调用视图上的公共方法来通知 GUI 在选择后更改视图。

也许我做错了,组合框不应该只包含模型,而是控制器(可以访问模型和视图),

组合框的侦听器将成为控件的一部分,是的。这将可以访问模型和视图。

在这种情况下,我可以只调用控制器的视图方法,该方法将从模型中获取所需的数据,将其传递给查看和显示它。

我不确定您的程序本身的细节是否能够回答这个问题。


编辑 1
在查看您的代码时,我发现了一个潜在问题:

    private static String[] allBuildingsTypes = {"School", "Hospital", 
            "Stadion"};
    private ArrayList<BuildingModel> buildings = new ArrayList<BuildingModel>();

您不应该在此处使用字符串,而应该使用行为良好的面向对象的 Building 类,该类具有名称 String,以及在您的 Building 对象需要的任何属性中重要的所有其他属性。然后你的模型应该处理建筑物对象的集合而不是建筑物的字符串表示。您的视图将显示这些建筑物的属性,并允许用户更新您允许他们更改的属性。

这涉及到程序结构的核心,因此我觉得非常重要,这样做可能需要您更改所有内容,但从长远来看,这是非常值得的。

编辑 1b:我的立场是正确的。看起来您有一个用于此的类 BuildingModel,其代码尚未在此处发布。我想知道您的 BuildingTypes 是否应该是属于您的 BuildingModel 类的枚举。


编辑 2
有关更多 Swing-MVC 相关代码的答案,请查看以下链接:

于 2013-05-08T21:40:29.587 回答