web-dev-qa-db-ja.com

JavaおよびGUI-ActionListenerはMVCパターンに従ってどこに属しますか?

私は現在、テンプレートJava=アプリケーションを書いています。MVCパターンを明確に追跡したい場合、どういうわけか、ActionListenerがどこに属しているかわかりません。

この例はSwingベースですが、フレームワークについてではなく、任意のフレームワークを使用してGUIを作成する、JavaでのMVCの基本概念です。

私は、JFrameとJButtonを含む非常に単純なアプリケーションから始めました(フレームを破棄してアプリケーションを閉じるため)。この投稿に続くコード。私たちが話していることを明確にするためだけに、特別なことは何もありません。この質問は私を悩ませすぎたので、私はまだモデルから始めませんでした。

次のような同様の質問がすでに1つ以上あります。
多くのActionListenerを持つMVCパターン
Javaスイング-ActionListenerはどこに行くべきですか?

しかし、私が2つのことを知りたいので、それらのどれも本当に満足していませんでした:

  • すべてのActionListenerを個別のパッケージに含めることは妥当ですか?
    • ViewとControllerを読みやすくするために、特にそうしたいと思います。リスナーが多い場合
  • リスナーがコントローラー内のサブクラスでない場合、ActionListener内からコントローラー関数をどのように実行しますか? (フォローアップ質問)

これが私がここで尋ねているあまり一般的でも曖昧でもないことを願っていますが、それはしばらくの間私に考えさせます。私は常に自分の方法で自分の方法を使用して、ActionHandlerにコントローラーを知らせましたが、これは正しくないようです。これが適切に行われる方法。

敬具、
ジェイソン


コントローラ:

package controller;

import Java.awt.event.ActionEvent;
import Java.awt.event.ActionListener;

import view.MainView;

public class MainController
{
    MainView mainView = new MainView();

    public MainController()
    {
        this.initViewActionListeners();
    }

    private void initViewActionListeners()
    {
        mainView.initButtons(new CloseListener());
    }

    public class CloseListener implements ActionListener
    {
        @Override
        public void actionPerformed(ActionEvent e)
        {
            mainView.dispose();
        }
    }
}


見る:

package view;

import Java.awt.Dimension;
import Java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class MainView extends JFrame
{
    JButton button_close    = new JButton();
    JPanel  panel_mainPanel = new JPanel();

    private static final long   serialVersionUID    = 5791734712409634055L;

    public MainView()
    {
        setDefaultCloseOperation(DISPOSE_ON_CLOSE);
        this.setSize(500, 500);
        this.add(panel_mainPanel);
        setVisible(true);
    }

    public void initButtons(ActionListener actionListener)
    {
        this.button_close = new JButton("Close");
        this.button_close.setSize(new Dimension(100, 20));
        this.button_close.addActionListener(actionListener);
        this.panel_mainPanel.add(button_close);
    }
}
19
jaySon

Swingは純粋なMVC実装ではないため、ビューとコントローラーが混在しているため、Swingで回答するのは非常に難しい質問です。

技術的には、モデルとコントローラーは相互作用でき、コントローラーとビューは相互作用できるはずですが、ビューとモデルは決して相互作用しないはずです。これは明らかにSwingの動作とは異なりますが、それは別の議論です...

もう1つの問題は、UIコンポーネントを誰にも公開したくない場合です。コントローラーは、特定のアクションがどのように発生するかを気にする必要はありません。

これは、UIコントロールにアタッチされているActionListenersがビューによって維持される必要があることを示唆しています。その後、ビューは何らかのアクションが発生したことをコントローラーに警告する必要があります。これには、コントローラーがサブスクライブする、ビューによって管理される別のActionListenerを使用できます。

さらに良いことに、このビューが生成する可能性のあるアクションを説明する専用のビューリスナーがあります。たとえば...

public interface MainViewListener {
    public void didPerformClose(MainView mainView);
}

次に、コントローラーはこのリスナーを介してビューをサブスクライブし、ビューは(この場合は)閉じるボタンが押されたときにdidPerformCloseを呼び出します。

この例でも、実装が提供することが保証されているプロパティ(セッターとゲッター)とアクション(リスナー/コールバック)を記述した「メインビュー」インターフェースを作成したくなりますが、これらはどのようにしても構いません。アクションが発生するのは、アクションが発生したときにのみ、何かを実行することが期待される場合です...

自問したい各レベルで、別のインスタンスの要素を変更する(モデルまたはコントローラーまたはビューを変更する)のは簡単ですか?コードを分離する必要がある場合は、問題があります。インターフェイスを介して通信し、レイヤー間の結合の量、および各レイヤーが他のレイヤーについて知っている量を、契約を維持している程度まで削減します

更新...

これを例にとってみましょう...

Login

実際には2つのビューがあり(実際のダイアログを割引します)、資格情報ビューとログインビューがあります。

CredentialsView

資格情報ビューは、認証される詳細、ユーザー名とパスワードを収集する責任があります。コントローラーは、「ログイン」ボタンを有効にするなどのアクションを実行する可能性があるため、これらの資格情報が変更されたときにコントローラーに情報を提供します...

ビューは、フィールドが無効になるため、認証がいつ行われるのかを知りたいので、ユーザーは認証が行われている間はビューを更新できません。同様に、いつ認証が行われるのかを知る必要があります。これらの不測の事態に対処する必要があるため、失敗または成功します。

public interface CredentialsView {

    public String getUserName();
    public char[] getPassword();

    public void willAuthenticate();
    public void authenticationFailed();
    public void authenticationSucceeded();

    public void setCredentialsViewController(CredentialsViewController listener);

}

public interface CredentialsViewController {

    public void credientialsDidChange(CredentialsView view);

}

CredentialsPane

CredentialsPaneCredentialsViewの物理的な実装であり、コントラクトを実装しますが、独自の内部状態を管理します。コントラクトの管理方法はコントローラーにとって重要ではありません。サポートされるコントラクトのみが考慮されます...

public class CredentialsPane extends JPanel implements CredentialsView {

    private CredentialsViewController controller;

    private JTextField userNameField;
    private JPasswordField passwordField;

    public CredentialsPane(CredentialsViewController controller) {
        setCredentialsViewController(controller);
        setLayout(new GridBagLayout());
        userNameField = new JTextField(20);
        passwordField = new JPasswordField(20);

        GridBagConstraints gbc = new GridBagConstraints();
        gbc.gridx = 0;
        gbc.gridy = 0;
        gbc.insets = new Insets(2, 2, 2, 2);
        gbc.anchor = GridBagConstraints.EAST;
        add(new JLabel("Username: "), gbc);

        gbc.gridy++;
        add(new JLabel("Password: "), gbc);

        gbc.gridx = 1;
        gbc.gridy = 0;
        gbc.anchor = GridBagConstraints.WEST;
        gbc.fill = GridBagConstraints.HORIZONTAL;
        add(userNameField, gbc);
        gbc.gridy++;
        add(passwordField, gbc);

        DocumentListener listener = new DocumentListener() {
            @Override
            public void insertUpdate(DocumentEvent e) {
                getCredentialsViewController().credientialsDidChange(CredentialsPane.this);
            }

            @Override
            public void removeUpdate(DocumentEvent e) {
                getCredentialsViewController().credientialsDidChange(CredentialsPane.this);
            }

            @Override
            public void changedUpdate(DocumentEvent e) {
                getCredentialsViewController().credientialsDidChange(CredentialsPane.this);
            }
        };

        userNameField.getDocument().addDocumentListener(listener);
        passwordField.getDocument().addDocumentListener(listener);

    }

    @Override
    public CredentialsViewController getCredentialsViewController() {
        return controller;
    }

    @Override
    public String getUserName() {
        return userNameField.getText();
    }

    @Override
    public char[] getPassword() {
        return passwordField.getPassword();
    }

    @Override
    public void willAuthenticate() {
        userNameField.setEnabled(false);
        passwordField.setEnabled(false);
    }

    @Override
    public void authenticationFailed() {
        userNameField.setEnabled(true);
        passwordField.setEnabled(true);

        userNameField.requestFocusInWindow();
        userNameField.selectAll();

        JOptionPane.showMessageDialog(this, "Authentication has failed", "Error", JOptionPane.ERROR_MESSAGE);
    }

    @Override
    public void authenticationSucceeded() {
        // Really don't care, but you might want to stop animation, for example...
    }

    public void setCredentialsViewController(CredentialsViewController controller){
        this.controller = controller;
    }

}

LoginView

LoginViewCredentialsViewの管理を担当しますが、認証が必要な場合や、何らかの方法でユーザーによってプロセスがキャンセルされた場合にLoginViewControllerに通知することもできます。 。

同様に、LoginViewControllerは、認証が行われる直前、および認証が失敗したか成功したかをビューに通知します。

public interface LoginView {

    public CredentialsView getCredentialsView();

    public void willAuthenticate();
    public void authenticationFailed();
    public void authenticationSucceeded();

    public void dismissView();

    public LoginViewController getLoginViewController();

}

public interface LoginViewController {

    public void authenticationWasRequested(LoginView view);
    public void loginWasCancelled(LoginView view);

}

LoginPane

LoginPaneは特別なもので、LoginViewControllerのビューとして機能しますが、CredentialsViewのコントローラーとしても機能します。これは重要です。ビューをコントローラーにできないということは何もないからです。しかし、このように実装することが常に意味があるとは限らないため、そのようなものを実装する方法には注意が必要です。ただし、2つのビューは情報を収集してイベントを管理するために一緒に作業することは、この場合には理にかなっています。

LoginPaneCredentialsViewの変更に基づいて自身の状態を変更する必要があるため、この場合はLoginPaneをコントローラーとして機能させることは理にかなっています。 、ボタンの状態を制御するメソッドをさらに提供する必要がありますが、これにより、UIロジックがコントローラーにブリードされ始めます...

public static class LoginPane extends JPanel implements LoginView, CredentialsViewController {

    private LoginViewController controller;
    private CredentialsPane credientialsView;

    private JButton btnAuthenticate;
    private JButton btnCancel;

    private boolean wasAuthenticated;

    public LoginPane(LoginViewController controller) {
        setLoginViewController(controller);
        setLayout(new BorderLayout());
        setBorder(new EmptyBorder(8, 8, 8, 8));

        btnAuthenticate = new JButton("Login");
        btnCancel = new JButton("Cancel");

        JPanel buttons = new JPanel();
        buttons.add(btnAuthenticate);
        buttons.add(btnCancel);

        add(buttons, BorderLayout.SOUTH);

        credientialsView = new CredentialsPane(this);
        add(credientialsView);

        btnAuthenticate.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                getLoginViewController().authenticationWasRequested(LoginPane.this);
            }
        });
        btnCancel.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                getLoginViewController().loginWasCancelled(LoginPane.this);
                // I did think about calling dispose here,
                // but's not really the the job of the cancel button to decide what should happen here...
            }
        });

        validateCreientials();

    }

    public static boolean showLoginDialog(LoginViewController controller) {

        final LoginPane pane = new LoginPane(controller);

        JDialog dialog = new JDialog();
        dialog.setTitle("Login");
        dialog.setModal(true);
        dialog.add(pane);
        dialog.pack();
        dialog.setLocationRelativeTo(null);
        dialog.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
        dialog.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                pane.getLoginViewController().loginWasCancelled(pane);
            }
        });
        dialog.setVisible(true);

        return pane.wasAuthenticated();

    }

    public boolean wasAuthenticated() {
        return wasAuthenticated;
    }

    public void validateCreientials() {

        CredentialsView view = getCredentialsView();
        String userName = view.getUserName();
        char[] password = view.getPassword();
        if ((userName != null && userName.trim().length() > 0) && (password != null && password.length > 0)) {

            btnAuthenticate.setEnabled(true);

        } else {

            btnAuthenticate.setEnabled(false);

        }

    }

    @Override
    public void dismissView() {
        SwingUtilities.windowForComponent(this).dispose();
    }

    @Override
    public CredentialsView getCredentialsView() {
        return credientialsView;
    }

    @Override
    public void willAuthenticate() {
        getCredentialsView().willAuthenticate();
        btnAuthenticate.setEnabled(false);
    }

    @Override
    public void authenticationFailed() {
        getCredentialsView().authenticationFailed();
        validateCreientials();
        wasAuthenticated = false;
    }

    @Override
    public void authenticationSucceeded() {
        getCredentialsView().authenticationSucceeded();
        validateCreientials();
        wasAuthenticated = true;
    }

    public LoginViewController getLoginViewController() {
        return controller;
    }

    public void setLoginViewController(LoginViewController controller) {
        this.controller = controller;
    }

    @Override
    public void credientialsDidChange(CredentialsView view) {
        validateCreientials();
    }

}

実施例

import Java.awt.BorderLayout;
import Java.awt.EventQueue;
import Java.awt.GridBagConstraints;
import Java.awt.GridBagLayout;
import Java.awt.Insets;
import Java.awt.event.ActionEvent;
import Java.awt.event.ActionListener;
import Java.awt.event.WindowAdapter;
import Java.awt.event.WindowEvent;
import Java.util.Random;
import Java.util.logging.Level;
import Java.util.logging.Logger;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPasswordField;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.EmptyBorder;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import Sun.net.www.protocol.http.HttpURLConnection;

public class Test {

    protected static final Random AUTHENTICATION_Oracle = new Random();

    public static void main(String[] args) {
        new Test();
    }

    public interface CredentialsView {
        public String getUserName();
        public char[] getPassword();
        public void willAuthenticate();
        public void authenticationFailed();
        public void authenticationSucceeded();
        public CredentialsViewController getCredentialsViewController();
    }

    public interface CredentialsViewController {
        public void credientialsDidChange(CredentialsView view);
    }

    public interface LoginView {
        public CredentialsView getCredentialsView();
        public void willAuthenticate();
        public void authenticationFailed();
        public void authenticationSucceeded();
        public void dismissView();
        public LoginViewController getLoginViewController();
    }

    public interface LoginViewController {
        public void authenticationWasRequested(LoginView view);
        public void loginWasCancelled(LoginView view);
    }

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                LoginViewController controller = new LoginViewController() {

                    @Override
                    public void authenticationWasRequested(LoginView view) {
                        view.willAuthenticate();
                        LoginAuthenticator authenticator = new LoginAuthenticator(view);
                        authenticator.authenticate();
                    }

                    @Override
                    public void loginWasCancelled(LoginView view) {

                        view.dismissView();

                    }
                };

                if (LoginPane.showLoginDialog(controller)) {

                    System.out.println("You Shell pass");

                } else {

                    System.out.println("You Shell not pass");

                }

                System.exit(0);

            }
        });
    }

    public class LoginAuthenticator {

        private LoginView view;

        public LoginAuthenticator(LoginView view) {
            this.view = view;
        }

        public void authenticate() {

            Thread t = new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException ex) {
                        Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
                    }
                    SwingUtilities.invokeLater(new Runnable() {
                        @Override
                        public void run() {
                            if (AUTHENTICATION_Oracle.nextBoolean()) {
                                view.authenticationSucceeded();
                                view.dismissView();
                            } else {
                                view.authenticationFailed();
                            }
                        }
                    });
                }
            });
            t.start();

        }

    }

    public static class LoginPane extends JPanel implements LoginView, CredentialsViewController {

        private LoginViewController controller;
        private CredentialsPane credientialsView;

        private JButton btnAuthenticate;
        private JButton btnCancel;

        private boolean wasAuthenticated;

        public LoginPane(LoginViewController controller) {
            setLoginViewController(controller);
            setLayout(new BorderLayout());
            setBorder(new EmptyBorder(8, 8, 8, 8));

            btnAuthenticate = new JButton("Login");
            btnCancel = new JButton("Cancel");

            JPanel buttons = new JPanel();
            buttons.add(btnAuthenticate);
            buttons.add(btnCancel);

            add(buttons, BorderLayout.SOUTH);

            credientialsView = new CredentialsPane(this);
            add(credientialsView);

            btnAuthenticate.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    getLoginViewController().authenticationWasRequested(LoginPane.this);
                }
            });
            btnCancel.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    getLoginViewController().loginWasCancelled(LoginPane.this);
                    // I did think about calling dispose here,
                    // but's not really the the job of the cancel button to decide what should happen here...
                }
            });

            validateCreientials();

        }

        public static boolean showLoginDialog(LoginViewController controller) {

            final LoginPane pane = new LoginPane(controller);

            JDialog dialog = new JDialog();
            dialog.setTitle("Login");
            dialog.setModal(true);
            dialog.add(pane);
            dialog.pack();
            dialog.setLocationRelativeTo(null);
            dialog.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
            dialog.addWindowListener(new WindowAdapter() {
                @Override
                public void windowClosing(WindowEvent e) {
                    pane.getLoginViewController().loginWasCancelled(pane);
                }
            });
            dialog.setVisible(true);

            return pane.wasAuthenticated();

        }

        public boolean wasAuthenticated() {
            return wasAuthenticated;
        }

        public void validateCreientials() {

            CredentialsView view = getCredentialsView();
            String userName = view.getUserName();
            char[] password = view.getPassword();
            if ((userName != null && userName.trim().length() > 0) && (password != null && password.length > 0)) {

                btnAuthenticate.setEnabled(true);

            } else {

                btnAuthenticate.setEnabled(false);

            }

        }

        @Override
        public void dismissView() {
            SwingUtilities.windowForComponent(this).dispose();
        }

        @Override
        public CredentialsView getCredentialsView() {
            return credientialsView;
        }

        @Override
        public void willAuthenticate() {
            getCredentialsView().willAuthenticate();
            btnAuthenticate.setEnabled(false);
        }

        @Override
        public void authenticationFailed() {
            getCredentialsView().authenticationFailed();
            validateCreientials();
            wasAuthenticated = false;
        }

        @Override
        public void authenticationSucceeded() {
            getCredentialsView().authenticationSucceeded();
            validateCreientials();
            wasAuthenticated = true;
        }

        public LoginViewController getLoginViewController() {
            return controller;
        }

        public void setLoginViewController(LoginViewController controller) {
            this.controller = controller;
        }

        @Override
        public void credientialsDidChange(CredentialsView view) {
            validateCreientials();
        }

    }

    public static class CredentialsPane extends JPanel implements CredentialsView {

        private CredentialsViewController controller;

        private JTextField userNameField;
        private JPasswordField passwordField;

        public CredentialsPane(CredentialsViewController controller) {
            setCredentialsViewController(controller);
            setLayout(new GridBagLayout());
            userNameField = new JTextField(20);
            passwordField = new JPasswordField(20);

            GridBagConstraints gbc = new GridBagConstraints();
            gbc.gridx = 0;
            gbc.gridy = 0;
            gbc.insets = new Insets(2, 2, 2, 2);
            gbc.anchor = GridBagConstraints.EAST;
            add(new JLabel("Username: "), gbc);

            gbc.gridy++;
            add(new JLabel("Password: "), gbc);

            gbc.gridx = 1;
            gbc.gridy = 0;
            gbc.anchor = GridBagConstraints.WEST;
            gbc.fill = GridBagConstraints.HORIZONTAL;
            add(userNameField, gbc);
            gbc.gridy++;
            add(passwordField, gbc);

            DocumentListener listener = new DocumentListener() {
                @Override
                public void insertUpdate(DocumentEvent e) {
                    getCredentialsViewController().credientialsDidChange(CredentialsPane.this);
                }

                @Override
                public void removeUpdate(DocumentEvent e) {
                    getCredentialsViewController().credientialsDidChange(CredentialsPane.this);
                }

                @Override
                public void changedUpdate(DocumentEvent e) {
                    getCredentialsViewController().credientialsDidChange(CredentialsPane.this);
                }
            };

            userNameField.getDocument().addDocumentListener(listener);
            passwordField.getDocument().addDocumentListener(listener);

        }

        @Override
        public CredentialsViewController getCredentialsViewController() {
            return controller;
        }

        @Override
        public String getUserName() {
            return userNameField.getText();
        }

        @Override
        public char[] getPassword() {
            return passwordField.getPassword();
        }

        @Override
        public void willAuthenticate() {
            userNameField.setEnabled(false);
            passwordField.setEnabled(false);
        }

        @Override
        public void authenticationFailed() {
            userNameField.setEnabled(true);
            passwordField.setEnabled(true);

            userNameField.requestFocusInWindow();
            userNameField.selectAll();

            JOptionPane.showMessageDialog(this, "Authentication has failed", "Error", JOptionPane.ERROR_MESSAGE);
        }

        @Override
        public void authenticationSucceeded() {
            // Really don't care, but you might want to stop animation, for example...
        }

        public void setCredentialsViewController(CredentialsViewController controller) {
            this.controller = controller;
        }

    }

}
17
MadProgrammer

それらはコントロールに関連付けられていますが、コントロールの直接の一部である必要はありません。たとえば、匿名の内部クラスとカップリングに関する別の質問のために準備していた下記のコードを参照してください。ここではすべてのボタンに匿名の内部アクション(もちろんActionListeners)を指定し、アクションを使用して変更しますGUIの状態。 GUI(コントロール)のリスナーは、この変更を通知され、それに応じて動作できます。

import Java.awt.*;
import Java.awt.event.*; Java.beans.PropertyChangeEvent;
import Java.beans.PropertyChangeListener;

import javax.swing.*;
import javax.swing.event.SwingPropertyChangeSupport;

public class AnonymousInnerEg2 {
   private static void createAndShowUI() {
      GuiModel2 model = new GuiModel2();
      GuiPanel2 guiPanel = new GuiPanel2();
      GuiControl2 guiControl = new GuiControl2();
      guiControl.setGuiPanel(guiPanel);
      guiControl.setGuiModel(model);
      try {
         guiControl.init();
      } catch (GuiException2 e) {
         e.printStackTrace();
         System.exit(-1);
      }

      JFrame frame = new JFrame("AnonymousInnerEg");
      frame.getContentPane().add(guiPanel);
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.pack();
      frame.setLocationRelativeTo(null);
      frame.setVisible(true);
   }

   public static void main(String[] args) {
      Java.awt.EventQueue.invokeLater(new Runnable() {
         public void run() {
            createAndShowUI();
         }
      });
   }
}

enum GuiState {
   BASE("Base"), START("Start"), END("End");
   private String name;

   private GuiState(String name) {
      this.name = name;
   }

   public String getName() {
      return name;
   }

}

class GuiModel2 {
   public static final String STATE = "state";
   private SwingPropertyChangeSupport support = new SwingPropertyChangeSupport(this);
   private GuiState state = GuiState.BASE;

   public GuiState getState() {
      return state;
   }

   public void setState(GuiState state) {
      GuiState oldValue = this.state;
      GuiState newValue = state;
      this.state = state;
      support.firePropertyChange(STATE, oldValue, newValue);
   }

   public void addPropertyChangeListener(PropertyChangeListener l) {
      support.addPropertyChangeListener(l);
   }

   public void removePropertyChangeListener(PropertyChangeListener l) {
      support.removePropertyChangeListener(l);
   }
}

@SuppressWarnings("serial")
class GuiPanel2 extends JPanel {
   public static final String STATE = "state";
   private String state = GuiState.BASE.getName();
   private JLabel stateField = new JLabel("", SwingConstants.CENTER);

   public GuiPanel2() {

      JPanel btnPanel = new JPanel(new GridLayout(1, 0, 5, 0));
      for (final GuiState guiState : GuiState.values()) {
         btnPanel.add(new JButton(new AbstractAction(guiState.getName()) {
            {
               int mnemonic = (int) getValue(NAME).toString().charAt(0);
               putValue(MNEMONIC_KEY, mnemonic);
            }

            @Override
            public void actionPerformed(ActionEvent e) {
               String name = getValue(NAME).toString();
               setState(name);
            }
         }));
      }

      setLayout(new BorderLayout());
      add(stateField, BorderLayout.PAGE_START);
      add(btnPanel, BorderLayout.CENTER);
   }

   public String getState() {
      return state;
   }

   public void setState(String state) {
      String oldValue = this.state;
      String newValue = state;
      this.state = state;
      firePropertyChange(STATE, oldValue, newValue);
   }

   public void setStateField(String name) {
      stateField.setText(name);
   }

}

class GuiControl2 {
   private GuiPanel2 guiPanel;
   private GuiModel2 model;
   private boolean allOK = false;

   public void setGuiPanel(GuiPanel2 guiPanel) {
      this.guiPanel = guiPanel;
      guiPanel.addPropertyChangeListener(GuiPanel2.STATE,
            new GuiPanelStateListener());
   }

   public void init() throws GuiException2 {
      if (model == null) {
         throw new GuiException2("Model is null");
      }
      if (guiPanel == null) {
         throw new GuiException2("GuiPanel is null");
      }
      allOK = true;
      guiPanel.setStateField(model.getState().getName());
   }

   public void setGuiModel(GuiModel2 model) {
      this.model = model;
      model.addPropertyChangeListener(new ModelListener());
   }

   private class GuiPanelStateListener implements PropertyChangeListener {
      @Override
      public void propertyChange(PropertyChangeEvent evt) {
         if (!allOK) {
            return;
         }
         if (GuiPanel2.STATE.equals(evt.getPropertyName())) {
            String text = guiPanel.getState();
            model.setState(GuiState.valueOf(text.toUpperCase()));
         }
      }
   }

   private class ModelListener implements PropertyChangeListener {
      @Override
      public void propertyChange(PropertyChangeEvent evt) {
         if (!allOK) {
            return;
         }
         if (GuiModel2.STATE.equals(evt.getPropertyName())) {
            GuiState state = (GuiState) evt.getNewValue();
            guiPanel.setStateField(state.getName());
         }
      }
   }
}

@SuppressWarnings("serial")
class GuiException2 extends Exception {

   public GuiException2() {
      super();
   }

   public GuiException2(String message) {
      super(message);
   }
}

警告に注意してください:私はプロのコーダーでも大学の訓練を受けたコーダーでもないので、これは私の意見としてのみ取り扱ってください。

現在学習中のA Java=学校。教師は、リスナーは常にControllerクラス内で宣言する必要があると私たちに言いました。私が行う方法は、 listeners()などのメソッドを実装します。内部には、匿名クラスを使用したすべてのリスナー宣言があります。これは、先生が見たい方法ですが、率直に言って、すべてが正しいかどうかはわかりません。

0
WayneEra