82 votes

Le modèle MVC et Swing

L'un des modèles de conception que je trouve le plus difficile à maîtriser dans la "vraie vie de Swing" est le modèle MVC. J'ai lu un bon nombre des articles de ce site qui traitent de ce modèle, mais je n'ai toujours pas l'impression de bien comprendre comment tirer parti de ce modèle dans mon application Java Swing.

Disons que j'ai un JFrame qui contient un tableau, quelques champs de texte et quelques boutons. J'utiliserais probablement un TableModel pour "relier" le tableau JT avec un modèle de données sous-jacent. Cependant, toutes les fonctions responsables de l'effacement des champs, de la validation des champs, du verrouillage des champs ainsi que les actions des boutons seraient généralement placées directement dans le JFrame. Toutefois, cela ne mélange-t-il pas le contrôleur et la vue du modèle ?

D'après ce que je peux voir, je parviens à mettre en œuvre "correctement" le modèle MVC lorsque je regarde la JTable (et le modèle), mais les choses se brouillent lorsque je regarde le JFrame dans son ensemble.

J'aimerais vraiment savoir comment les autres s'y prennent à cet égard. Comment procédez-vous lorsque vous devez afficher un tableau, quelques champs et quelques boutons à un utilisateur en utilisant le modèle MVC ?

2 votes

Voici un lien ejemplo .

0 votes

Pour tous ceux qui viennent à cette fête - Swing n'est PAS un pur MVC - il emprunte beaucoup au concept, mais "réduit" la "vue et le contrôleur" ensemble.

110voto

Dhruv Gairola Points 4448

Un livre que je vous recommanderais vivement pour MVC en swing serait "Head First Design Patterns" de Freeman et Freeman. Ils ont une explication très complète de MVC.

Résumé

  1. Vous êtes l'utilisateur, vous interagissez avec la vue. La vue est votre fenêtre sur le modèle. Quand vous faites quelque chose à la vue (comme cliquer sur le bouton bouton Play), la vue indique au contrôleur ce que vous avez fait. C'est le contrôleur de s'en occuper.

  2. Le contrôleur demande au modèle de changer son état. Le contrôleur prend vos actions et les interprète. Si vous cliquez sur un bouton, c'est le travail du contrôleur de comprendre ce que cela signifie et comment le modèle doit être manipulé en fonction de cette action.

  3. Le contrôleur peut également demander à la vue de changer. Lorsque le contrôleur reçoit une action de la part de la vue, il peut avoir besoin de dire à la vue de changer en conséquence. vue de changer en conséquence. Par exemple, le contrôleur peut activer ou désactiver certains boutons ou éléments de menu dans l'interface.

  4. Le modèle notifie la vue lorsque son état a changé. Quand quelque chose change dans le modèle, en fonction d'une action que vous avez entreprise (comme cliquer sur un bouton) ou d'un autre changement interne (comme la prochaine chanson suivante dans la liste de lecture a commencé), le modèle notifie à la vue que son état a changé.

  5. La vue demande l'état au modèle. La vue obtient l'état qu'elle affiche directement du modèle. Par exemple, lorsque le modèle notifie à la vue qu'une nouvelle chanson a commencé à être jouée, la vue demande le nom de la chanson au modèle et l'affiche. La vue peut également demander l'état du modèle en tant que résultat du contrôleur demandant un changement dans la vue.

enter image description here Source : (Au cas où vous vous demanderiez ce qu'est un "contrôleur crémeux", pensez à un biscuit Oreo, le contrôleur étant le centre crémeux, la vue étant le biscuit supérieur et le modèle étant le biscuit inférieur).

Au cas où cela vous intéresserait, vous pouvez télécharger une chanson assez divertissante sur le modèle MVC sur le site suivant ici !

Un problème que vous pouvez rencontrer avec la programmation Swing consiste à amalgamer le fil SwingWorker et EventDispatch avec le modèle MVC. En fonction de votre programme, votre vue ou votre contrôleur peut devoir étendre le SwingWorker et remplacer la fonction de distribution d'événements par une autre. doInBackground() où la logique à forte intensité de ressources est placée. Ce modèle peut être facilement fusionné avec le modèle MVC typique, et est typique des applications Swing.

EDIT #1 :

En outre, il est important de considérer le MVC comme une sorte de composite de divers modèles. Par exemple, votre modèle pourrait être implémenté à l'aide du modèle Observer (nécessitant que la vue soit enregistrée comme observateur du modèle) tandis que votre contrôleur pourrait utiliser le modèle Strategy.

EDIT #2 :

Je voudrais en outre répondre spécifiquement à votre question. Vous devriez afficher vos boutons de table, etc. dans la vue, qui mettrait évidemment en œuvre un ActionListener. Dans votre actionPerformed() vous détectez l'événement et l'envoyez à une méthode connexe dans le contrôleur (n'oubliez pas que la vue contient une référence au contrôleur). Ainsi, lorsqu'un bouton est cliqué, l'événement est détecté par la vue, envoyé à la méthode du contrôleur, ce dernier pouvant demander directement à la vue de désactiver le bouton ou autre. Ensuite, le contrôleur va interagir avec le modèle et le modifier (qui aura principalement des méthodes getter et setter, et d'autres pour enregistrer et notifier les observateurs, etc.) Dès que le modèle est modifié, il appelle une mise à jour sur les observateurs enregistrés (ce sera la vue dans votre cas). Ainsi, la vue se mettra à jour elle-même.

0 votes

... le JFrame est un composant, et non une feuille. typiquement, les mises à jour faites par le contrôleur sont envoyées au JFrame, qui s'occupe du reste, donc, cela pourrait donner l'illusion d'être un contrôleur, mais en réalité, ce n'est pas le cas parce qu'il n'a pas changé le modèle, seulement la vue. si votre JFrame a en quelque sorte changé le modèle directement - vous le faites mal.

0 votes

... encore une fois, le mot-clé ici est "directement". Dans votre cas, vous pourriez écouter les clics de souris sur la table, et envoyer la logique aux méthodes du contrôleur qui modifient le modèle de la table.

2 votes

@DhruvGairola La description du deuxième point est pour le troisième point, le troisième et pour points ont les mêmes descriptions dupliquées. Pouvez-vous les corriger s'il vous plaît.

36voto

onepotato Points 1446

Je ne suis pas fan de l'idée que la vue doit être celle qui est notifiée par le modèle lorsque ses données changent. Je déléguerais cette fonctionnalité au contrôleur. Dans ce cas, si vous changez la logique de l'application, vous n'avez pas besoin d'intervenir sur le code de la vue. La tâche de la vue ne concerne que les composants de l'application + la mise en page, rien de plus, rien de moins. La mise en page en swing est déjà une tâche verbeuse, pourquoi la laisser interférer avec la logique de l'application ?

Mon idée de MVC (avec laquelle je travaille actuellement, jusqu'ici tout va bien) est :

  1. La vue est la plus stupide des trois. Elle ne sait rien du contrôleur et du modèle. Elle ne se préoccupe que de la prosthétique et de la disposition des composants de l'oscillation.
  2. Le modèle est également stupide, mais pas autant que la vue. Il réalise les fonctionnalités suivantes.
  • a. lorsque l'un de ses setter est appelé par le contrôleur, il enverra une notification à ses listeners/observers (comme je l'ai dit, je confierais ce rôle au contrôleur). Je préfère Support SwingPropertyChangeSupport pour y parvenir, car il est déjà optimisé à cet effet.
  • b. la fonctionnalité d'interaction avec la base de données.
  1. Un contrôleur très intelligent. Il connaît très bien la vue et le modèle. Le contrôleur a deux fonctionnalités :
  • a. Il définit l'action que la vue exécutera lorsque l'utilisateur interagira avec elle.
  • b. Il écoute le modèle. Comme je l'ai dit, lorsque le setter du modèle est appelé, le modèle envoie une notification au contrôleur. C'est le travail du contrôleur d'interpréter cette notification. Il peut avoir besoin de refléter le changement dans la vue.

Exemple de code

The View :

Comme je l'ai dit, la création de la vue est déjà verbeuse, alors créez simplement votre propre implémentation :)

interface View{
    JTextField getTxtFirstName();
    JTextField getTxtLastName();
    JTextField getTxtAddress();
}

Il est idéal d'interfacer les trois pour des raisons de testabilité. Je n'ai fourni que mon implémentation de Model et Controller.

Le modèle :

public class MyImplementationOfModel implements Model{
    ...
    private SwingPropertyChangeSupport propChangeFirer;
    private String address;
    private String firstName;
    private String lastName;

    public MyImplementationOfModel() {
        propChangeFirer = new SwingPropertyChangeSupport(this);
    }
    public void addListener(PropertyChangeListener prop) {
        propChangeFirer.addPropertyChangeListener(prop);
    }
    public void setAddress(String address){
        String oldVal = this.address;
        this.address = address;

        //after executing this, the controller will be notified that the new address has been set. Its then the controller's
        //task to decide what to do when the address in the model has changed. Ideally, the controller will update the view about this
        propChangeFirer.firePropertyChange("address", oldVal, address);
    }
    ...
    //some other setters for other properties & code for database interaction
    ...
}

Le contrôleur :

public class MyImplementationOfController implements PropertyChangeListener, Controller{

    private View view;
    private Model model;

    public MyImplementationOfController(View view, Model model){
        this.view = view;
        this.model = model;

        //register the controller as the listener of the model
        this.model.addListener(this);

        setUpViewEvents();
    }

    //code for setting the actions to be performed when the user interacts to the view.
    private void setUpViewEvents(){
        view.getBtnClear().setAction(new AbstractAction("Clear") { 
            @Override
            public void actionPerformed(ActionEvent arg0) {
                model.setFirstName("");
                model.setLastName("");
                model.setAddress("");
            }
        });

        view.getBtnSave().setAction(new AbstractAction("Save") { 
            @Override
            public void actionPerformed(ActionEvent arg0) {
                ...
                //validate etc.
                ...
                model.setFirstName(view.getTxtFName().getText());
                model.setLastName(view.getTxtLName().getText());
                model.setAddress(view.getTxtAddress().getText());
                model.save();
            }
        });
    }

    public void propertyChange(PropertyChangeEvent evt){
        String propName = evt.getPropertyName();
        Object newVal = evt.getNewValue();

        if("address".equalsIgnoreCase(propName)){
            view.getTxtAddress().setText((String)newVal);
        }
        //else  if property (name) that fired the change event is first name property
        //else  if property (name) that fired the change event is last name property
    }
}

Le Main, où le MVC est installé :

public class Main{
    public static void main(String[] args){
        View view = new YourImplementationOfView();
        Model model = new MyImplementationOfModel();

        ...
        //create jframe
        //frame.add(view.getUI());
        ...

        //make sure the view and model is fully initialized before letting the controller control them.
        Controller controller = new MyImplementationOfController(view, model);

        ...
        //frame.setVisible(true);
        ...
    }
}

4 votes

Intéressant mais moins efficace lorsqu'un modèle d'entité unique est affiché dans plusieurs vues... Dans ce cas, votre conception peut conduire à un "gros contrôleur" gérant un seul modèle mais gérant toutes les vues liées. Et cela devient encore plus délicat si vous essayez de réutiliser un ensemble de "petit modèle", grâce à une agrégation en un "grand modèle" parce qu'une vue affiche des informations distribuées dans plusieurs entités "petit modèle".

1 votes

@onepotato Je viens d'essayer vos codes. Lorsque j'appuie sur un bouton, je peux faire en sorte que les codes dans setUpViewEvents() se déclenchent. Cependant, lorsque je fais un model.setSomething(123), les codes dans propertyChange ne se déclenchent pas. J'ai même placé un println directement sous Object newVal = evt.getNewValue() ; et il ne s'imprime pas.

10 votes

Ce n'est PAS le MVC modèle architectural, mais le étroitement liés MVP (Modèle-Vue-Présentateur). Dans un MVC typique, c'est précisément le travail du modèle de notifier la vue quand il a changé exactement ce que vous "n'aimez pas". Regardez ce schéma pour voir comment fonctionnent les interactions dans un MVC typique.

26voto

René Link Points 9136

Le modèle MVC est un modèle de la façon dont une interface utilisateur peut être structurée. Il définit donc les 3 éléments Modèle, Vue, Contrôleur :

  • Modèle Un modèle est une abstraction de quelque chose qui est présentée à l'utilisateur. Dans Swing, on distingue les modèles d'interface graphique et les modèles de données. Les modèles d'interface utilisateur graphique abstraient l'état d'un composant de l'interface utilisateur, par exemple Modèle de bouton . Les modèles de données permettent d'abstraire les données structurées que l'interface utilisateur présente à l'utilisateur, telles que TableModel .
  • Voir La vue est un composant de l'interface utilisateur qui est responsable de la présentation des données à l'utilisateur. Elle est donc responsable de toutes les questions liées à l'interface utilisateur, comme la mise en page, le dessin, etc. Par exemple JTableau .
  • Contrôleur Un contrôleur encapsule le code de l'application qui est exécuté en réponse à une interaction de l'utilisateur (mouvement de la souris, clic de la souris, pression sur une touche, etc.) Les contrôleurs peuvent avoir besoin d'entrées pour leur exécution et ils produisent des sorties. Ils lisent leurs entrées à partir de modèles et mettent à jour les modèles à la suite de l'exécution. Ils peuvent également restructurer l'interface utilisateur (par exemple, remplacer des composants de l'interface ou afficher une toute nouvelle vue). Cependant, ils ne doivent pas connaître les composants de l'interface utilisateur, car vous pouvez encapsuler la restructuration dans une interface distincte que le contrôleur ne fait qu'invoquer. En Swing, un contrôleur est normalement implémenté par un ActionListener o Action .

Ejemplo

  • Rouge = modèle
  • Vert = vue
  • Bleu = contrôleur

enter image description here

Lorsque le Button est cliqué, il invoque le ActionListener . El ActionListener ne dépend que des autres modèles. Il utilise certains modèles comme entrée et d'autres comme résultat ou sortie. C'est comme les arguments d'une méthode et les valeurs de retour. Les modèles notifient l'interface utilisateur lorsqu'ils sont mis à jour. Il n'est donc pas nécessaire que la logique du contrôleur connaisse le composant de l'interface utilisateur. Les objets modèles ne connaissent pas l'interface utilisateur. La notification est effectuée par un modèle d'observateur. Ainsi, les objets du modèle savent seulement qu'il y a quelqu'un qui veut être notifié si le modèle change.

Dans Java Swing, il existe des composants qui mettent en œuvre un modèle et un contrôleur. Par exemple, le javax.swing.Action . Il implémente un modèle d'interface utilisateur (propriétés : activation, petite icône, nom, etc.) et est un contrôleur parce qu'il prolonge ActionListener .

A explication détaillée, exemple d'application et code source : https://www.link-intersystems.com/blog/2013/07/20/the-mvc-pattern-implemented-with-java-swing/ .

Les bases du MVC en moins de 260 lignes :

import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.event.ActionEvent;
import java.util.ArrayList;
import java.util.List;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.DefaultListModel;
import javax.swing.DefaultListSelectionModel;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.ListSelectionModel;
import javax.swing.WindowConstants;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.PlainDocument;

public class Main {

    public static void main(String[] args) {
        JFrame mainFrame = new JFrame("MVC example");
        mainFrame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        mainFrame.setSize(640, 300);
        mainFrame.setLocationRelativeTo(null);

        PersonService personService = new PersonServiceMock();

        DefaultListModel searchResultListModel = new DefaultListModel();
        DefaultListSelectionModel searchResultSelectionModel = new DefaultListSelectionModel();
        searchResultSelectionModel
                .setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
        Document searchInput = new PlainDocument();

        PersonDetailsAction personDetailsAction = new PersonDetailsAction(
                searchResultSelectionModel, searchResultListModel);
        personDetailsAction.putValue(Action.NAME, "Person Details");

        Action searchPersonAction = new SearchPersonAction(searchInput,
                searchResultListModel, personService);
        searchPersonAction.putValue(Action.NAME, "Search");

        Container contentPane = mainFrame.getContentPane();

        JPanel searchInputPanel = new JPanel();
        searchInputPanel.setLayout(new BorderLayout());

        JTextField searchField = new JTextField(searchInput, null, 0);
        searchInputPanel.add(searchField, BorderLayout.CENTER);
        searchField.addActionListener(searchPersonAction);

        JButton searchButton = new JButton(searchPersonAction);
        searchInputPanel.add(searchButton, BorderLayout.EAST);

        JList searchResultList = new JList();
        searchResultList.setModel(searchResultListModel);
        searchResultList.setSelectionModel(searchResultSelectionModel);

        JPanel searchResultPanel = new JPanel();
        searchResultPanel.setLayout(new BorderLayout());
        JScrollPane scrollableSearchResult = new JScrollPane(searchResultList);
        searchResultPanel.add(scrollableSearchResult, BorderLayout.CENTER);

        JPanel selectionOptionsPanel = new JPanel();

        JButton showPersonDetailsButton = new JButton(personDetailsAction);
        selectionOptionsPanel.add(showPersonDetailsButton);

        contentPane.add(searchInputPanel, BorderLayout.NORTH);
        contentPane.add(searchResultPanel, BorderLayout.CENTER);
        contentPane.add(selectionOptionsPanel, BorderLayout.SOUTH);

        mainFrame.setVisible(true);
    }

}

class PersonDetailsAction extends AbstractAction {

    private static final long serialVersionUID = -8816163868526676625L;

    private ListSelectionModel personSelectionModel;
    private DefaultListModel personListModel;

    public PersonDetailsAction(ListSelectionModel personSelectionModel,
            DefaultListModel personListModel) {
        boolean unsupportedSelectionMode = personSelectionModel
                .getSelectionMode() != ListSelectionModel.SINGLE_SELECTION;
        if (unsupportedSelectionMode) {
            throw new IllegalArgumentException(
                    "PersonDetailAction can only handle single list selections. "
                            + "Please set the list selection mode to ListSelectionModel.SINGLE_SELECTION");
        }
        this.personSelectionModel = personSelectionModel;
        this.personListModel = personListModel;
        personSelectionModel
                .addListSelectionListener(new ListSelectionListener() {

                    public void valueChanged(ListSelectionEvent e) {
                        ListSelectionModel listSelectionModel = (ListSelectionModel) e
                                .getSource();
                        updateEnablement(listSelectionModel);
                    }
                });
        updateEnablement(personSelectionModel);
    }

    public void actionPerformed(ActionEvent e) {
        int selectionIndex = personSelectionModel.getMinSelectionIndex();
        PersonElementModel personElementModel = (PersonElementModel) personListModel
                .get(selectionIndex);

        Person person = personElementModel.getPerson();
        String personDetials = createPersonDetails(person);

        JOptionPane.showMessageDialog(null, personDetials);
    }

    private String createPersonDetails(Person person) {
        return person.getId() + ": " + person.getFirstName() + " "
                + person.getLastName();
    }

    private void updateEnablement(ListSelectionModel listSelectionModel) {
        boolean emptySelection = listSelectionModel.isSelectionEmpty();
        setEnabled(!emptySelection);
    }

}

class SearchPersonAction extends AbstractAction {

    private static final long serialVersionUID = 4083406832930707444L;

    private Document searchInput;
    private DefaultListModel searchResult;
    private PersonService personService;

    public SearchPersonAction(Document searchInput,
            DefaultListModel searchResult, PersonService personService) {
        this.searchInput = searchInput;
        this.searchResult = searchResult;
        this.personService = personService;
    }

    public void actionPerformed(ActionEvent e) {
        String searchString = getSearchString();

        List<Person> matchedPersons = personService.searchPersons(searchString);

        searchResult.clear();
        for (Person person : matchedPersons) {
            Object elementModel = new PersonElementModel(person);
            searchResult.addElement(elementModel);
        }
    }

    private String getSearchString() {
        try {
            return searchInput.getText(0, searchInput.getLength());
        } catch (BadLocationException e) {
            return null;
        }
    }

}

class PersonElementModel {

    private Person person;

    public PersonElementModel(Person person) {
        this.person = person;
    }

    public Person getPerson() {
        return person;
    }

    @Override
    public String toString() {
        return person.getFirstName() + ", " + person.getLastName();
    }
}

interface PersonService {

    List<Person> searchPersons(String searchString);
}

class Person {

    private int id;
    private String firstName;
    private String lastName;

    public Person(int id, String firstName, String lastName) {
        this.id = id;
        this.firstName = firstName;
        this.lastName = lastName;
    }

    public int getId() {
        return id;
    }

    public String getFirstName() {
        return firstName;
    }

    public String getLastName() {
        return lastName;
    }

}

class PersonServiceMock implements PersonService {

    private List<Person> personDB;

    public PersonServiceMock() {
        personDB = new ArrayList<Person>();
        personDB.add(new Person(1, "Graham", "Parrish"));
        personDB.add(new Person(2, "Daniel", "Hendrix"));
        personDB.add(new Person(3, "Rachel", "Holman"));
        personDB.add(new Person(4, "Sarah", "Todd"));
        personDB.add(new Person(5, "Talon", "Wolf"));
        personDB.add(new Person(6, "Josephine", "Dunn"));
        personDB.add(new Person(7, "Benjamin", "Hebert"));
        personDB.add(new Person(8, "Lacota", "Browning "));
        personDB.add(new Person(9, "Sydney", "Ayers"));
        personDB.add(new Person(10, "Dustin", "Stephens"));
        personDB.add(new Person(11, "Cara", "Moss"));
        personDB.add(new Person(12, "Teegan", "Dillard"));
        personDB.add(new Person(13, "Dai", "Yates"));
        personDB.add(new Person(14, "Nora", "Garza"));
    }

    public List<Person> searchPersons(String searchString) {
        List<Person> matches = new ArrayList<Person>();

        if (searchString == null) {
            return matches;
        }

        for (Person person : personDB) {
            if (person.getFirstName().contains(searchString)
                    || person.getLastName().contains(searchString)) {
                matches.add(person);
            }

        }
        return matches;
    }
}

MVC Basics Screencast

4 votes

J'aime cette réponse +1 , pour la mention Action comme Controller en fait, je pense que tout EventListener sont des contrôleurs..

0 votes

@nachokk Oui, en effet. Comme je l'ai dit A controller encapsulates the application code that is executed in order to an user interaction . Déplacer la souris, cliquer sur un composant, appuyer sur une touche, etc. sont autant d'interactions avec l'utilisateur. Pour être plus clair, j'ai mis à jour ma réponse.

2voto

Konrad Garus Points 19280

Vous pouvez créer le modèle dans une classe Java distincte et simple, et le contrôleur dans une autre.

Vous pouvez ensuite y ajouter des composants Swing. JTable serait l'une des vues (et le modèle de table serait de facto faire partie de la vue - elle serait seulement traduite du "modèle partagé" vers JTable ).

Chaque fois que la table est modifiée, son modèle de table indique au "contrôleur principal" de mettre à jour quelque chose. Cependant, le contrôleur ne doit rien savoir de la table. L'appel devrait donc ressembler à ceci updateCustomer(customer, newValue) pas updateCustomer(row, column, newValue) .

Ajouter une interface d'écoute (observateur) pour le modèle partagé. Certains composants (par exemple, votre table) pourraient l'implémenter directement. Un autre observateur pourrait être le contrôleur qui coordonne la disponibilité des boutons, etc.


C'est une façon de procéder, mais vous pouvez bien sûr la simplifier ou l'étendre si elle est trop lourde pour votre cas d'utilisation.

Vous pouvez fusionner le contrôleur avec le modèle et faire en sorte que la même classe traite les mises à jour et maintienne la disponibilité des composants. Vous pouvez même faire en sorte que le "modèle partagé" soit un TableModel (bien que si elle n'est pas seulement utilisée par la table, je recommanderais au moins de fournir une API plus conviviale qui ne divulgue pas les abstractions de la table).

D'autre part, vous pouvez avoir des interfaces complexes pour les mises à jour ( CustomerUpdateListener , OrderItemListener , OrderCancellationListener ) et un contrôleur (ou médiateur) dédié uniquement à la coordination des différentes vues.

Cela dépend de la complexité de votre problème.

0 votes

Environ 90% de toutes les vues consistent en un tableau où l'utilisateur peut sélectionner un élément à modifier. Ce que j'ai fait jusqu'à présent, c'est que j'ai un modèle de données par lequel passent toutes les opérations CRUD. J'utilise un TableModel pour adapter le modèle de données à la JTable. Ainsi, pour mettre à jour un élément, j'invoquerais table.getModel().getModel().update(Element e). En d'autres termes, le JTable est en quelque sorte le contrôleur pour le moment. Toutes les actions des boutons sont placées dans des classes séparées (je les réutilise dans des contextes différents) et font leur travail à travers les méthodes du modèle sous-jacent. Est-ce une conception viable ?

1voto

AndyT Points 1034

Pour une séparation adéquate, vous disposez généralement d'une classe de contrôleur à laquelle la classe Frame est déléguée. Il existe plusieurs façons de configurer les relations entre les classes : vous pouvez implémenter un contrôleur et l'étendre à votre classe de vue principale, ou utiliser une classe de contrôleur autonome que le cadre appelle lorsque des événements se produisent. La vue reçoit généralement les événements du contrôleur en implémentant une interface d'écoute.

Il arrive qu'une ou plusieurs parties du modèle MVC soient triviales, ou tellement "minces" qu'il est inutilement complexe de les séparer. Si votre contrôleur est rempli d'appels d'une ligne, le fait de le placer dans une classe distincte peut finir par obscurcir le comportement sous-jacent. Par exemple, si tous les événements que vous traitez sont liés à un modèle TableModel et sont de simples opérations d'ajout et de suppression, vous pouvez choisir d'implémenter toutes les fonctions de manipulation du tableau dans ce modèle (ainsi que les rappels nécessaires pour l'afficher dans le JTable). Ce n'est pas un véritable MVC, mais cela évite d'ajouter de la complexité là où elle n'est pas nécessaire.

Quelle que soit la manière dont vous l'implémentez, n'oubliez pas de JavaDoc vos classes, méthodes et paquets afin que les composants et leurs relations soient correctement décrits !

0 votes

@AndyT bien que la plupart de vos explications soient bonnes, j'ai un problème avec votre conseil de combiner le modèle avec le contrôleur. que se passe-t-il si je veux soudainement changer le contrôleur ? je découvre alors que vous avez couplé le modèle avec le contrôleur et que vous devez également modifier le modèle. votre code n'est plus extensible. aussi court soit votre contrôleur, je ne le combinerais pas avec un modèle. ou une vue.

0 votes

Je ne suis pas en désaccord - cela dépend beaucoup de votre application. Si votre modèle n'est pas plus sophistiqué qu'un objet Liste, et que votre contrôleur ne fait guère plus qu'ajouter et supprimer des éléments, créer trois classes distinctes (modèle Liste, contrôleur et adaptateur pour que votre modèle fonctionne avec le JTable) est excessif. Il est plus facile de remanier le modèle dans le cas improbable où vous auriez besoin d'un contrôleur différent, que de créer des classes de shim pour répondre à un besoin futur inconnu.

0 votes

@AndyT est d'accord, peut-être que si votre application est petite, c'est le moyen le plus rapide. Mais pour l'extensibilité (considérez que l'ajout n'est pas fait par le même programmeur), cela pourrait être un désavantage.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X