3

Java アプリケーションに MVC を実装する方法を理解しようとしており、いくつかのチュートリアルと MVC の hello world を読んでいますが、まだ多くのことがわかりません。これらすべてを理解するのを手伝っていただければ幸いです。

さまざまな建物の保存と操作に使用される単純な GUI アプリケーションがあるとします。リストからタイプを選択してボタンを押すと、建物を追加できます。これらのビルディングはいくつかのアレイリストに保存され、GUI に表示され、編集可能になります (部屋の数、フロアの数 ..... 重要ではありません)。建物がJComboBoxに表示され、建物を選択すると、その建物の設定パネルが表示されます。

これまでのところ、2つの「コンポーネント」があります。建物(コンテナ)と建物BuildingsModelクラスを作成しました。これは建物を保持し、それらを操作するメソッドをいくつか持ち、変更後にオブザーバーに通知します。それから、 BuildingsModel を監視するBuildingsViewクラスがあります。次に、BuildingsModel と BuildingsView をパラメーターとして取り、ビューをオブザーバーとしてモデルにバインドし、いくつかの初期建物を作成し、いくつかのリスナーをビューに追加する、コンストラクター メソッドだけを持つBuldingsControllerクラスを用意します。

今、私は続ける方法がわかりません。あまり嬉しくない点がいくつかあります。

  1. リスナーをボタンにバインドしました。これにより、ビュー内の JList から現在の選択が取得され、新しいオブジェクト (xxxBuildingModel) が作成され、BuildingsModel に追加されます。ただし、JList には、すべてのビルド タイプの文字列表現のみが含まれており、長い if-else ステートメントを回避し、その文字列に適したクラスを見つけるために、リフレクションを使用する必要がありました。(各建物の種類には、 BuildingModelを拡張する独自のクラスがあります。)それを行うより良い方法はありますか?

  2. 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 に String を保持させるのではなく、Building (非 GUI) オブジェクトを保持させます。ListCellRenderer を使用して、各建物を最適に表示する方法を JList に指示します。次に、ユーザーが JList から項目を選択すると、実際の建物オブジェクトが選択され、その String 表現は選択されません。確かに、これについての反省は考慮しないでください。

2 番目のリスナーは、作成済みの構築インスタンスを含む 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 に答える