web-dev-qa-db-fra.com

Java et GUI - Où les ActionListeners appartiennent-ils selon le modèle MVC?

J'écris actuellement un modèle Java et en quelque sorte, je ne sais pas où appartiennent les ActionListeners si je voulais suivre proprement le modèle MVC.

L'exemple est basé sur Swing, mais il ne s'agit pas du framework mais plutôt du concept de base de MVC en Java, utilisant n'importe quel framework pour créer une interface graphique.

J'ai commencé avec une application absolument simple contenant un JFrame et un JButton (pour disposer le cadre donc fermer l'application). Le code qui suit ce post. Rien de vraiment spécial, juste pour clarifier ce dont nous parlons. Je n'ai pas encore commencé avec le modèle car cette question me dérangeait trop.

Il y a déjà eu plus d'une question similaire, comme celles-ci:
modèle MVC avec de nombreux ActionListeners
Java swing - Où doit aller l'ActionListener?

Mais aucun d'eux n'était vraiment satisfaisant car j'aimerais savoir deux choses:

  • Est-il raisonnable d'avoir tous les ActionListeners dans un package séparé?
    • Je voudrais le faire dans un souci de lisibilité de View et Controller, en particulier. s'il y a beaucoup d'auditeurs
  • Comment pourrais-je exécuter une fonction Controller à partir d'un ActionListener, si l'écouteur n'est pas une sous-classe à l'intérieur du Controller? (question complémentaire)

J'espère que ce n'est pas trop général ou vague que je demande ici, mais ça me fait réfléchir depuis un moment maintenant. J'ai toujours utilisé une sorte de à ma façon, laissant l'ActionHandler connaître le contrôleur , mais cela ne semble pas correct, donc j'aimerais enfin savoir comment cela se fait correctement.

Sincères amitiés,
JaySon


Manette:

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();
        }
    }
}


Vue:

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

C'est une question très difficile à répondre avec Swing, car Swing n'est pas une implémentation MVC pure, la vue et le contrôleur sont mélangés.

Techniquement, un modèle et un contrôleur devraient pouvoir interagir et le contrôleur et la vue devraient pouvoir interagir, mais la vue et le modèle ne devraient jamais interagir, ce qui n'est clairement pas le fonctionnement de Swing, mais c'est un autre débat ...

Un autre problème est que vous ne voulez vraiment pas exposer les composants de l'interface utilisateur à quiconque, le contrôleur ne devrait pas se soucier de la façon dont certaines actions se produisent, seulement qu'il le peut.

Cela suggère que les ActionListener attachés à vos contrôles d'interface utilisateur devraient être conservés par la vue. La vue devrait alors alerter le contrôleur qu'une sorte d'action s'est produite. Pour cela, vous pouvez utiliser un autre ActionListener, géré par la vue, auquel le contrôleur est abonné.

Mieux encore, j'aurais un écouteur de vue dédié, qui décrit les actions que cette vue pourrait produire, par exemple ...

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

Le contrôleur s'abonne alors à la vue via cet écouteur et la vue appelle didPerformClose lorsque (dans ce cas) le bouton de fermeture est enfoncé.

Même dans cet exemple, je serais tenté de créer une interface "vue principale", qui décrit les propriétés (setters et getters) et les actions (listeners/callbacks) que toute implémentation est garantie de fournir, alors vous ne vous souciez pas de la façon dont ces des actions se produisent, seulement que lorsqu'elles le font, vous devez faire quelque chose ...

À chaque niveau que vous souhaitez vous poser, serait-il facile de changer un élément (changer le modèle ou le contrôleur ou la vue) pour une autre instance? Si vous devez découpler le code, vous avez un problème. Communiquez via des interfaces et essayez de réduire la quantité de couplage entre les couches et la quantité que chaque couche connaît des autres au point où elles maintiennent simplement des contrats

mis à jour ...

Prenons cela pour un exemple ...

Login

Il y a en fait deux vues (actualisation de la boîte de dialogue réelle), il y a la vue des informations d'identification et la vue de connexion, oui, elles sont différentes comme vous le verrez.

Informations d'identification

La vue des informations d'identification est chargée de collecter les détails à authentifier, le nom d'utilisateur et le mot de passe. Il fournira des informations au contrôleur pour lui faire savoir quand ces informations d'identification ont été modifiées, car le contrôleur peut vouloir prendre des mesures, comme activer le bouton "connexion" ...

La vue voudra également savoir quand l'authentification est sur le point d'avoir lieu, car elle voudra désactiver ses champs, de sorte que l'utilisateur ne peut pas mettre à jour la vue pendant que l'authentification a lieu, également, il devra savoir quand l'authentification échoue ou réussit, car il devra prendre des mesures pour faire face à ces éventualités.

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);

}

Panneau d'informations d'identification

Le CredentialsPane est l'implémentation physique d'un CredentialsView, il implémente le contrat, mais gère son propre état interne. La façon dont le contrat est géré est sans importance pour le responsable du traitement, il ne se soucie que du contrat qui a été respecté ...

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

Le LoginView est responsable de la gestion d'un CredentialsView, mais aussi de la notification du LoginViewController quand l'authentification doit avoir lieu ou si le processus a été annulé par l'utilisateur, par certains moyens. .

De même, le LoginViewController indiquera à la vue quand l'authentification est sur le point d'avoir lieu et si l'authentification a échoué ou a réussi.

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

Le LoginPane est un peu spécial, il agit comme la vue pour le LoginViewController, mais il agit également comme le contrôleur pour le CredentialsView. C'est important, car rien ne dit qu'une vue ne peut pas être un contrôleur, mais je serais prudent sur la façon dont vous implémentez de telles choses, car il n'est pas toujours logique de le faire de cette façon, mais parce que les deux vues sont travailler ensemble pour recueillir des informations et gérer les événements, il était logique dans ce cas.

Étant donné que le LoginPane devra changer son propre état en fonction des modifications apportées au CredentialsView, il est logique d'autoriser le LoginPane à agir comme contrôleur dans ce cas, sinon , vous devez fournir plus de méthodes qui contrôlent cet état des boutons, mais cela commence à faire fondre la logique de l'interface utilisateur sur le contrôleur ...

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();
    }

}

Exemple de travail

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

Ils sont associés au contrôle, mais ils ne doivent pas nécessairement faire directement partie du contrôle. Par exemple, veuillez consulter le code affiché ci-dessous que je préparais pour une autre question, une sur les classes internes anonymes et le couplage, ici je donne à tous mes boutons des actions internes anonymes (qui sont des ActionListeners, bien sûr), puis j'utilise les actions pour changer l'état de l'interface graphique. Tous les auditeurs de l'interface graphique (le contrôle) seront informés de ce changement et pourront alors agir en conséquence.

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);
   }
}

Remarque cependant: je ne suis pas un codeur professionnel ou même un codeur formé à l'université, veuillez donc ne considérer cela que comme mon opinion.

Je suis en train d'apprendre Java à l'école. Les professeurs nous ont dit que les auditeurs doivent toujours être déclarés à l'intérieur de la classe Controller. La façon dont je le fais, c'est de implémenter une méthode, par exemple listeners (). À l'intérieur se trouvent toutes les déclarations des auditeurs utilisant des classes anonymes. C'est la façon dont mes professeurs veulent que cela voit, mais franchement, je ne suis pas vraiment sûr d'avoir tout compris .

0
WayneEra