It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center




7年前关闭。




我试图了解如何在我的Java应用程序中实现MVC,阅读一些教程和MVC hello世界,但是我仍然不确定很多事情,因此,如果您能帮助我理解所有这些内容,我将非常高兴。

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

到目前为止,我有2个“组件”。 建筑物(容器)和建筑物。我创建了 BuildingsModel 类,该类保存建筑物,并提供一些与建筑物配合使用的方法,并在更改后通知观察者。然后,我有一个 BuildingsView 类,它正在观察BuildingsModel。然后,我仅使用构造函数方法使用 BuldingsController 类,该类将BuildingsModel和BuildingsView作为参数,将视图绑定为模型作为观察者,创建一些初始建筑物并向该视图添加一些侦听器。

现在我不知道如何继续。有些事情我不是很高兴。
  • 我已将侦听器绑定到该按钮,该按钮将从视图中的JList获取当前选择,创建新对象(xxxBuildingModel)并将其添加到BuildingsModel。但是,JList仅包含所有建筑类型的String表示形式,并且为了避免冗长的if-else状态菜单和为该String找到正确的类,我不得不使用反射。 (每种建筑类型都有自己的类,扩展了 BuildingModel 。)有什么更好的方法吗?
  • 第二个侦听器绑定到包含已创建的建筑实例的JComboBox。在该组合框中选择建筑物后,我要显示该建筑物的设置表单。因此,我猜应该有一些与BuildingModel相关的视图类将显示其当前设置(模型的状态)。但是我不确定如何从BuildingsController的构造函数上下文中执行此操作。我只能访问建筑物的模型,因此如何为该实例“找到”正确的视图并显示它?也许我做错了一切,组合框不应该只包含模型,而应该包含控制器(可以访问模型和视图),在这种情况下,我可以调用控制器的view方法,该方法将从模型中获取所需的数据,将其传递以查看和显示。如果我应该将具体的建筑实例作为模型,作为控制器或作为封装所有这三个组件(控制器,模型,视图)的某些自定义类,则不使用...

  • 这是代码中最重要的部分。我没有包括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);
        }
    }
    

    谢谢 :)

    最佳答案

    我已将侦听器绑定到该按钮,该按钮将从视图中的JList获取当前选择,创建新对象(xxxBuildingModel)并将其添加到BuildingsModel。但是,JList仅包含所有建筑类型的String表示形式,并且为了避免冗长的if-else状态菜单和为该String找到正确的类,我不得不使用反射。 (每种建筑类型都有其自己的扩展BuildingModel的类。)还有更好的方法吗?

    是。不要让您的JList包含字符串,而是让它包含Building(非GUI)对象。使用ListCellRenderer告诉JList如何最好地显示每个建筑物。然后,当用户从JList中选择一项时,他们选择的是实际的建筑对象,而不是其String表示形式。当然,不要考虑任何反射Reflection 。

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

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

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

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

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

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

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

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

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

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

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

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

    编辑1b :我已纠正。看来您有一个与此相关的类,BuildingModel,该类的代码尚未在此处发布。我想知道您的BuildingTypes是否应该是BuildingModel类的一部分的枚举。

    编辑2
    有关与Swing-MVC相关的代码的更多答案,请查看以下链接:
  • Using a JFileChooser with Swing GUI classes and listeners:我创建的一个Swing MVC小示例。
  • GUI not working after rewriting to MVC:我不能赞扬垃圾桶的例子。您不应该阅读它,而应该研究它。
  • JAVA Swing MVC - Main Controller?
  • 07-24 15:11