Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

MVC Discussion :

[MVC] Liste de personnes


Sujet :

MVC

  1. #1
    Membre à l'essai
    Profil pro
    Inscrit en
    janvier 2004
    Messages
    17
    Détails du profil
    Informations personnelles :
    Localisation : Suisse

    Informations forums :
    Inscription : janvier 2004
    Messages : 17
    Points : 17
    Points
    17
    Par défaut [MVC] Liste de personnes
    Bonsoir à tous ceux qui n'ont pas la chance d'être en vacances

    Je travaille sur un programme en architecture MVC (java SE - desktop) et j'ai un soucis par rapport à une classe en particulier. C'est ma classe PersonListModel. Elle contient une liste de personnes ainsi que le numéro de l'indice sélectionné (donc de la personne sélectionnée).

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    public class PersonListModel extends AbstractModel {
    ...
      protected ArrayList<PersonData> personInfo;
      private int selectedPersonIndex;
    ... 
    
      public PersonData getSelectedPerson() {
          if (selectedPersonIndex != NO_SELECTED_PERSON) {
              return personInfo.get(selectedPersonIndex);
          }
          else {
              return null;
          }
      }
    
    ...
    
        // On lui passe le chemin contenant les fichiers de ressource
        // et le constructeur de PersonData va faire un parse des resources
        // pour récupérer les infos relatives à la personne.
        public void addPerson(String _destPath) {
            personInfo.add(new PersonData(_destPath));
            selectedPersonIndex = personInfo.size() - 1;
            notifyObservers();
        }
    
    ....
    }
    et la classe PersonData contient toutes les infos relatives à la personne (nom, prénom, age, adresse, entreprise, etc.).

    Dans ma classe PersonListModel, j'ai plusieurs classes qui manipulent les données relatives aux personnes comme l'ajout d'une nouvelle personne, la suppression, le changement de personne sélectionnée, etc. A chacune de ces méthodes, j'ai bien un notifyObserver() pour avertir mes vues du changement de données.

    Le contrôleur (PersonListController), là au milieu, se charge uniquement de vérifier si le chemin est correct, l'indice à sélectionner est valide, etc.

    Tout fonctionne très bien, excepté un détail important.

    Si le programmeur fait quelque part dans le code un :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    personListModel.getSelectedPerson().setName();
    alors ça va court-circuiter mon modèle puisque ça va directement modifier le nom de la personne sans passer par mes méthodes set du modèle et donc pas de notify !!

    J'ai vu dans plusieurs séquences de code sur le web que la liste de personnes devrait plutôt être gérée dans le PersonListController. Mais là aussi j'ai essayé de coder quelques choses du style mais ça me semble encore pire.

    Est-ce que qqn pourrait m'aiguiller et me dire quel est le problème dans mon architecture ? Ou comment structurer différemment ?

    Merci beaucoup d'avance.
    La patience est la plus héroïque des vertus...

  2. #2
    Membre habitué
    Profil pro
    Inscrit en
    août 2006
    Messages
    89
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : août 2006
    Messages : 89
    Points : 168
    Points
    168
    Par défaut
    Bonjour,

    Pour ma part je conçois MVC comme une ensemble de 3 boites noires : Le modèle, le contrôleur et la vue.

    En principe l'implémentation interne de ces boites ne doit pas être connue des autres boites. Les communications d'une boite avec les autres doit se limiter à une interface définie qui seule sera accessible depuis les autres boites.

    Typiquement un objet du modèle ne doit pas être accessible directement depuis le contrôleur ou la vue. De même, les modifications des données doivent être encapsulées et pas effectuées directement depuis une autre boite. Donc le problème principal est que personListModel retourne directement des objets internes au modèle.

    Il ne faut en aucun cas passer directement les objets internes aux autres boites. Pour échanger des données entre boite, il faut passer les données par valeur ou éventuellement via des objets ayant copiés les données et dont la durée de vie serait limitée à l'échange de ces données.

    Le problème de l'accès aux méthodes (mises à jour de la liste des personnes par exemple) peut se régler simplement avec le design pattern 'façade' qui sera la seule interface de communication pour les autres boites. Cette façade (la classe PersonListModel après refactoring peut être?) ne doit bien sûr donner aucun moyen de récupérer directement les objets internes, elle doit uniquement permettre de récupérer des données ou mettre à jour des données.

    NB : il peut y avoir une façade différente pour chaque boite externe. Par exemple le modèle peut contenir une façade pour le contrôleur et une façade pour la vue. Ca permet à la vue d’accéder aux données uniquement en lecture, tout en permettant au contrôleur d'accéder au méthodes encapsulant la modification des données.

  3. #3
    Provisoirement toléré
    Homme Profil pro
    Inscrit en
    août 2002
    Messages
    143
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : août 2002
    Messages : 143
    Points : 261
    Points
    261
    Par défaut
    Citation Envoyé par tristan_m Voir le message
    Typiquement un objet du modèle ne doit pas être accessible directement depuis le contrôleur ou la vue
    Je ne suis pas tout à fait d'accord, puis passer par un système de façade ne me semble pas indispensable.

    La boite modèle gère les opérations CRUD avec ton système de persistance : Créer, Récupérer, Mettre à jour, Supprimer.

    La vue affiche les objets du modèle.

    Le contrôleur permet de faire le lien entre ta vue et le modèle en informant le modèle si opérations à effectuer, et notifie la vue si une opération a été effectuée.

    Pour ton souci lianoos, ton MVC est étrange. Tu le dis toi même tu as deux model : PersonModel et PersonListModel. Pour ton besoin je ne suis pas sur que ce soit nécessaire. Peut-être doit tu remplacer ton PersonListModel par un vrai controlleur.

  4. #4
    Membre habitué
    Profil pro
    Inscrit en
    août 2006
    Messages
    89
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : août 2006
    Messages : 89
    Points : 168
    Points
    168
    Par défaut
    Bonjour,

    Utiliser une façade n'est évidemment qu'une suggestion.
    En revanche, l'accès direct aux objets du modèle implique deux choses :
    • Cela introduit un couplage entre le modèle et la vue/le contrôleur : Si l'implémentation interne du modèle change (méthode ou classe supprimée par exemple), la vue et le contrôleur ne marcheront peut être plus.
    • Le problème que soulève lianoos, à savoir que si l'on peut accéder directement aux objets et à leur méthodes, on court-circuite l'intégrité du modèle.


    Après, tout dépend du niveau de robustesse recherché. Le système étant déjà fonctionnel, un commentaire ou une mise à jour de la documentation peuvent être suffisants si la durée de vie et l'évolution du projet sont maîtrisés.

  5. #5
    Membre à l'essai
    Profil pro
    Inscrit en
    janvier 2004
    Messages
    17
    Détails du profil
    Informations personnelles :
    Localisation : Suisse

    Informations forums :
    Inscription : janvier 2004
    Messages : 17
    Points : 17
    Points
    17
    Par défaut
    Un grand merci à vous 2 pour vos réponses C'est vraiment sympa.

    @tmanta7: J'ai corrigé et complété le poste initial. Excuse-moi j'avais une fois mal nommé le modèle. Ce qui faisait croire que j'avais 2 modèles, mais ce n'est pas le cas.

    Non, je n'aimerais pas laisser mon MVC tel quel, car justement il va évoluer, le code va se complexifier et du coup le court-circuitage va encore plus se fondre dans la masse et générer des erreurs, j'en suis sure.

    Donc si je comprends bien, mon "erreur" est de manipuler mon objet PersonData depuis ailleurs de le modèle. Le problème c'est que j'ai relu et je l'utilise à beaucoup d'endroit... Et donc ce que tu me conseilles tristan c'est d'utiliser une façade là-entre pour éviter que le type ne soit retourné directement ?

    Je crains juste une chose, c'est que mon field personInfo est sérialisé/désérialisé.

    @tmanta7 : Du coup après mes modifs du post initial, tu penses toujours que la façade est inutile ? Et si oui comment ferais-tu autrement ?

    A tout hasard, car j'imagine que ce cas de figure doit tout de même se présenter souvent, est-ce que vous auriez trouvé un exemple plus ou moins proche sur ? Car j'ai vraiment cherché longtemps un tuto ou un exemple, mais nada. Même pas trouvé un exemple qui utilise la solution de la façade.

    En attendant, je vais déjà étudier la façon de rajouter une façade, voir si j'arrive à l'implémenter
    La patience est la plus héroïque des vertus...

  6. #6
    Membre habitué
    Profil pro
    Inscrit en
    août 2006
    Messages
    89
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : août 2006
    Messages : 89
    Points : 168
    Points
    168
    Par défaut
    Bonjour,

    Comme je le précisais dans mon premier message, la communication "inter-boite" peut impliquer des objets communs aux trois boites.

    Pour prendre un exemple, un modèle gérant des rectangles, échangera à chaque communication un doublon (longueur, largeur) avec la vue et le controleur.
    On peut très bien créer une classe Rectangle qui contiendra ces deux variables comme membres afin d'échanger uniquement des objets de type Rectangle plutôt que 2 variables longueur et largeur.
    On perd en robustesse (si la classe Rectangle est modifiée il y aura potentiellement beaucoup de code à mettre à jour), mais on gagne en clarté de code.

    NB : Les classes de données (i.e. des classes possédant uniquement des membres publiques et aucune méthode) sont clairement indiquées pour remplir ce rôle.

    Dans votre cas, la classe personData semble correspondre à ce type d'objet.
    Il y aura peut être un peu de refactoring à faire afin que cette classe ne fasse QUE de l'encapsulation de données (typiquement le parsing de fichier devra être fait à l'extérieur de la classe).
    En principe à l'heure actuelle, seul le modèle créé ou modifie des objets personData, le contrôleur et la vue se contentant d'en lire le contenu. Le volume de code à remanier devrait donc être limité.

    Il y a un point auquel il faut faire attention cependant : les objets de type personData envoyés au contrôleur ou à la vue doivent être des COPIES des objets du modèle, pas les objets eux même.


    Concernant l'objet personInfo sérialisé/déserialisé, j'avoue ne pas avoir saisi le problème. PersonInfo est envoyé au contrôleur et/ou à la vue?


    Concernant d'éventuels exemples, je présume que la plupart se contentent d'un modèle très simple (une seule classe?), cas dans lequel l'implémentation d'une façade est effectivement superflue.
    L'interface commence à montrer son utilité lorsque la complexité du système augmente ou s'il est fortement sujet à des évolutions.
    Il y a par contre pas mal de chose concernant le design pattern 'façade', notamment grâce aux contributeurs de 'developpez' :
    http://rpouiller.developpez.com/tuto...-gang-of-four/


    Enfin, si vous partez sur l'idée de la façade, je vous conseille d'abord de commencer par la façade entre le modèle et le contrôleur.
    Une façade entre le modèle et la vue peut s'avérer délicate lorsque le design pattern 'observateur' est utilisé.
    S'assurer que les objets envoyés à la vue soient des copies est suffisant pour corriger le problème que vous avez rencontré.

    La façade entre le modèle et le contrôleur est relativement simple.
    Il faut déjà commencer par créer une interface (qui ne devra faire aucune référence aux objets internes du modèle excepté peut être personData).
    Le contrôleur n'aura accès qu'à cette interface, donc il suffit de lister les méthodes qui sont appelées par le contrôleur et les définir dans l'interface.
    Ensuite le modèle doit implémenter cette interface.
    L'implémentation fera juste la liaison entre la méthode appelée et l'objet interne du modèle (celui qui était directement appelé auparavant) qui s'occupera de faire concrètement l'opération.

  7. #7
    Membre à l'essai
    Profil pro
    Inscrit en
    janvier 2004
    Messages
    17
    Détails du profil
    Informations personnelles :
    Localisation : Suisse

    Informations forums :
    Inscription : janvier 2004
    Messages : 17
    Points : 17
    Points
    17
    Par défaut
    Mieux vaut tard que jamais...
    Je viens de faire le refactoring de mon fameux modèle. Je vous transmets mes modifs ça peut peut-être aider qqn.

    Voici ce que ça donne.

    - Ma classe PersonData qui devenait trop grande a été décomposées en plusieurs classes. J'ai PersonData (qui contient les données), PersonState (contient tout ce qui est relatif à l'état de la personne qui sont des données temporaires que je ne voulais pas sérialiser) et PersonResources (fichiers ou autres appartenant à la personne).

    - Tous les traitements qui étaient contenus dans PersonData ont été déplacés dans le modèle.

    - J'ai transformé mon modèle en façade. Alors mon modèle ne retourne plus de PersonData ou autre, il ne retourne plus que des int, String, etc. De plus, il ne fait qu'une petite partie des get de mes 3 composants ce qui permet aussi de simplifier/clarifier et de ne rendre accessibles que les méthodes utiles. Et pour ce qui était tout de même de retourner le nom, prénom, âge en une seule méthode get, j'ai créé une nouvelle classe (faisant office d'une structure) qui ne retourne qu'une partie des infos de PersonData.

    Du coup quand je fais un add ou un remove, le modèle se charge de manipuler les objets correspondants. Il fait tous les traitements lui-même et n'utilise que des get/set des composants (composants data, state ou resources).

    Encore merci à tous ceux qui m'ont aidé !
    La patience est la plus héroïque des vertus...

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Liste de personnes et checkbox associées
    Par sir_gcc dans le forum Struts 1
    Réponses: 5
    Dernier message: 24/05/2007, 12h56
  2. [PHP-JS] Problème liste de Personnes à ajouter
    Par katchi dans le forum Langage
    Réponses: 1
    Dernier message: 26/12/2006, 15h32
  3. Réponses: 3
    Dernier message: 22/11/2006, 18h49
  4. Réponses: 4
    Dernier message: 23/06/2006, 18h35

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo