IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
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

Dotnet Discussion :

Quelques questions sur le pattern MVC


Sujet :

Dotnet

  1. #21
    maa
    maa est déconnecté
    Membre éclairé
    Avatar de maa
    Inscrit en
    Octobre 2005
    Messages
    672
    Détails du profil
    Informations personnelles :
    Âge : 41

    Informations forums :
    Inscription : Octobre 2005
    Messages : 672
    Par défaut
    Merci pour ces précisions.

    En passant la liste de modèle à la vue, cela me permettrait également de rendre la vue utile pour afficher différentes collections de Shipping, n'est ce pas ?

    Sinon, il y a encore un truc qui me perturbe. Dans cette webcast (si tu peux la visionner rapidement), l'utilisation du MVC ne semble pas être la même que celle qu'on a faite. La vue ne ressemble pas à grand chose et c'est encore une fois le contrôleur qui la crée... Bref, j'ai l'impression qu'il y a différentes manières de faire du MVC.

    Enfin, il faut également que j'ajoute à mon modèle une class SpecialDeliveryDate qui ressemble à DeliveryDay sauf qu'elle possède une propriété Date (DateTime) au lieu d'une propriété Day (Int). De plus elle possède une propriété IsDeliveryPossible qui cette fois-ci du sens au niveau du modèle car un jour pour être férié (et donc livraison impossible) ou spécial pour d'autre raisons (mais livraison possible).
    Au début, j'avais donné à DeliveryDay et SpecialDeliveryDate un ancêtre commun contenant entre autre la propriété IsDeliveryPossible. Maintenant, je pense que ça n'en vaut plus la peine puisque IsDeliveryPossible n'a de sens que de DeliveryDayView...
    Bref, toujours est-il que je peux binder la class SpecialDeliveryDate telle quelle à l'interface (je n'ai pas besoin de ViewModel). Je pensais donc ajouter simplement à ShippingView une collection de SpecialDeliveryDate. Est-ce choquant ?

  2. #22
    Membre émérite
    Profil pro
    Inscrit en
    Septembre 2003
    Messages
    652
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2003
    Messages : 652
    Par défaut
    Citation Envoyé par maa Voir le message
    En passant la liste de modèle à la vue, cela me permettrait également de rendre la vue utile pour afficher différentes collections de Shipping, n'est ce pas ?
    Yup

    Citation Envoyé par maa Voir le message
    Sinon, il y a encore un truc qui me perturbe. Dans cette webcast (si tu peux la visionner rapidement), l'utilisation du MVC ne semble pas être la même que celle qu'on a faite. La vue ne ressemble pas à grand chose et c'est encore une fois le contrôleur qui la crée...
    Mmh, j'ai regardé en accéléré (et j'ai dû couper le son après le 42è "d'accord ?" pour éviter de frapper mon écran), mais ce qui est fait dans ce webcast-là (ça évolue peut-être dans les suivants, mais j'ai pas le courage de regarder :) n'a pas grand chose à voir avec du MVC.

    Déjà il n'y a pas de M. Le 'contrôleur' tape directement dans la couche d'accès aux données. Et la DAL, ce n'est pas le modèle. Le contrôleur ne contrôle d'ailleurs rien, mais vu que l'ensemble se limite à faire deux requêtes en collant le résultat dans deux collections, y a pas grand chose à contrôler de toute façon.

    Après, le 'CustomersAndRegionsView' dont le nom fait penser qu'il faudrait deux classes, ça correspond à tes DeliveryDayView et ShippingView. Pas le V de MVC, mais des éléments de présentation (view model, presentation model, peu importe le terme utilisé). La vraie vue, celle qui est rattachée au contrôleur, c'est la fenêtre elle-même. Celle qui, dans le webcast, crée le contrôleur et sur laquelle se font les actions que le contrôleur est censé gérer.

    Maintenant, il y a aussi ce problème :
    Citation Envoyé par maa Voir le message
    Bref, j'ai l'impression qu'il y a différentes manières de faire du MVC.
    Pour citer (encore et toujours) Fowler (encore et toujours la même page) :
    Citation Envoyé par Martin Fowler
    Probably the widest quoted pattern in UI development is Model View Controller (MVC) - it's also the most misquoted. I've lost count of the times I've seen something described as MVC which turned out to be nothing like it.
    Vu comment le terme de MVC a été maltraité et resservi à toutes les sauces depuis le temps, c'est pas étonnant qu'on ne s'y retrouve pas. Il fut un temps où le modèle WebForms se prétendait être du MVC. L'aspx était censé être la vue, le codebehind le contrôleur et... bah tout le bordel derrière le modèle. Pourtant, rien à voir.

    En ce qui me concerne, la façon dont j'ai parlé du MVC dans ce topic, c'est la façon dont je le vois et telle que je le comprends d'après la description de Fowler (que je prends donc comme référence). Vu qu'on est un peu juste entre nous deux sur le sujet, va savoir si ça correspond à l'idée que les habitués de ce forum en ont :)

    Si tu demandes à 10 personnes comment fonctionne le MVC, tu n'auras probablement pas 10 réponses différentes, mais certainement pas une seule non plus.

    En tout cas une chose est sûre et certaine, c'est que le code de ce webcast n'est *pas* du MVC :)

    Le contrôleur qui tape dans la base, c'est déjà pas bon, la vue qui va taper dans le contrôleur pour récupérer ce qu'elle doit afficher, c'est pas ça non plus.

    S'il y avait un intermédiaire entre le contrôleur et la BDD, et si c'était lui qui transmettait les collections à afficher à la vue, ça ferait du MVP. En l'état... je ne suis pas sûr qu'il y ait un nom.

    Citation Envoyé par maa Voir le message
    Bref, toujours est-il que je peux binder la class SpecialDeliveryDate telle quelle à l'interface (je n'ai pas besoin de ViewModel). Je pensais donc ajouter simplement à ShippingView une collection de SpecialDeliveryDate. Est-ce choquant ?
    Pas nécessairement. Le seul point sur lequel je verrais un risque c'est que si le contenu de cette collection est modifié dans l'interface, ça modifie directement des objets du modèle sans passer par le contrôleur ou une quelconque classe métier qui pourrait vérifier ce qui est fait.
    Selon le contexte, est-ce que c'est gênant ou risqué, est-ce que ça justifie de rajouter une nouvelle classe qui ne fera que renvoyer la même chose, je dirais que c'est à toi de décider :)

  3. #23
    maa
    maa est déconnecté
    Membre éclairé
    Avatar de maa
    Inscrit en
    Octobre 2005
    Messages
    672
    Détails du profil
    Informations personnelles :
    Âge : 41

    Informations forums :
    Inscription : Octobre 2005
    Messages : 672
    Par défaut
    J'ai dû m'interrompre sur ce projet pendant plusieurs jours.
    Merci pour ton dernier post qui a bien clarifié les choses de mon côté.
    Je me suis remis à mon projet aujourd'hui et j'ai finalement changé d'avis sur l'utilisation d'une collection de SpecialDeliveryDate dans ShippingView.
    J'ai finalement besoin d'utiliser une classe SpecialDeliveryDateView.
    Comment alors maintenant synchroniser la collection de SpecialDeliveryDateView de ShippingView et la collection de SpecialDeliveryDate de Shipping ? D'après ce que j'ai compris, il faudrait :

    - déclancher des événement PropertyChanged dans SpecialDeliveryDateView à chaque changement des propriété, les relayer dans la classe ShippingView. Le controleur les intercepte ensuite pour mettre à jour les propriété de l'objet SpecialDeliveryDate correspondant dans la collection de classe Shipping. Il faut alors que SpecialDeliveryDateView contienne une propriété publique qui est une instance de SpecialDeliveryDate pour pouvoir retrouver l'objet correspondant.
    Bon ça fait beaucoup de chemin pour mettre à jour le modèle et je me pose encore une fois la question de la réelle nécessité de passer ici par le contrôleur. D'autant plus qu'il n'y a pas de contrôle de données à faire ici (je met simplement à jour une propriété du model quand elle es modifiée dans la vue correspondante). Y a t-il un autre intérêt à passer par le contrôleur que de vérifier ce qui est fait avant de mettre à jour le model ? Si je peux me passer du contrôleur, la tentation est grande d'écrire mes propriété de la vue comme ceci :
    public string Infos
    {
    get { return model.Infos; }
    set { model.Infos= value; }
    }
    - Pour répercuter l'ajout/la suppression d'items dans la collection de la vue vers la collection du modèle, je déclenche un événement depuis la classe Shipping. Cette fois-ci j'ai besoin de passer par le contrôleur car j'utilise les méthodes AddSpecialDate() et RemoveSpecialDate() de la classe Shipping.

    Donc en gros 2 actions à faire pour synchroniser mes 2 collections et des événements de partout. Est-ce comme ça qu'il faut procéder ? Peut-on se permettre quelques allégement ?

    Désolé d'avoir laissé ce topic en suspens. J'espère que tu es encore dans le coup...

  4. #24
    Membre émérite
    Profil pro
    Inscrit en
    Septembre 2003
    Messages
    652
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2003
    Messages : 652
    Par défaut
    Question préalable : ton SpecialDeliveryDateView ressemble à quoi ? Est-ce que c'est une bête adaptation de SpecialDeliveryDate, avec filtrage/formatage interposé ou est-ce que c'est un bout d'interface graphique avec de vrais contrôles ?

    Si c'est juste une adaptation... ben comme son nom l'indique, je le vois bêtement comme un adaptateur entre un élément du modèle et l'interface graphique. À ce titre, qu'il tienne à jour l'instance de SpecialDeliveryDate correspondante semble logique.
    Si tu vois la vue (ton formulaire) comme le niveau le plus haut de ton application, le contrôleur et SpecialDeliveryDateView sont en-dessous (et le modèle encore après).

    Par contre si SpecialDeliveryDateView est un vrai bout d'interface (un contrôle quoi), là il faudrait continuer de passer par un contrôleur. Au plus 'pur', avoir un triplet MVC à ce niveau-là. En moins pur, transmettre les évènements à la vue principale qui permettra au contrôleur de s'occuper du reste (ce qui implique de fournir les infos nécessaires pour qu'il s'y retrouve).

    Vu ton exemple sur la propriété Infos, SpecialDeliveryDateView semble n'être qu'un adaptateur, donc ta grande tentation serait la mienne aussi :)


    Il ne faut pas perdre de vue que l'intérêt de MVC (et MVP & co) est la séparation des responsabilités et la possibilité de couvrir un maximum de traitements liés à l'interface par des tests unitaires. Il s'agit de séparer la plomberie WinForms/WPF/WebForms/whatever du reste.

    Si SpecialDeliveryDateView peut être testée en isolation, sans plomberie graphique, sans règles métier particulières, juste pour vérifier qu'elle présente tel objet de telle façon, c'est bon, elle a déjà sa propre responsabilité indépendante et testable.

  5. #25
    maa
    maa est déconnecté
    Membre éclairé
    Avatar de maa
    Inscrit en
    Octobre 2005
    Messages
    672
    Détails du profil
    Informations personnelles :
    Âge : 41

    Informations forums :
    Inscription : Octobre 2005
    Messages : 672
    Par défaut
    Oui je pense qu'il faut voir SpecialDeliveryDateView comme un adaptateur (que je devrais renommer SpecialDeliveryDateAdaptater ?)
    Et à priori, c'est la même chose pour les propriétés de DeliveryDayView (sauf IsDeliveryPossible qui n'existe pas dans l'entité métier correspondante et qui dont la modification se traduit par l'ajout/suppression d'un élément du modèle). Donc je suis tenté de synchroniser ces propriétés de DeliveryDayView de la même manière (sans passer par le contrôleur). Il me semble que tu me l'avais déconseillé au départ, mais c'était avant qu'on introduise le fait que la Form WPF est le vrai contrôle.

    Edit : en fait à priori ça n'est pas possible avec DeliveryDayView puisqu'il n'y a pas toujours un model correspondant pas de possibilité de relayer vers les propriété de ce dernier...

  6. #26
    Membre émérite
    Profil pro
    Inscrit en
    Septembre 2003
    Messages
    652
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2003
    Messages : 652
    Par défaut
    Citation Envoyé par maa Voir le message
    Oui je pense qu'il faut voir SpecialDeliveryDateView comme un adaptateur (que je devrais renommer SpecialDeliveryDateAdaptater ?)
    J'ai tendance à y penser comme à des vues des objets du domaine, donc mon premier réflexe est de suffixer par View, un peu comme ce qu'est une DataView par rapport à une DataTable. Mais c'est vrai que ça peut être source de confusion vis-à-vis de la vue MVC. Si Adapter te botte, ça colle aussi :)

    Citation Envoyé par maa Voir le message
    Edit : en fait à priori ça n'est pas possible avec DeliveryDayView puisqu'il n'y a pas toujours un model correspondant pas de possibilité de relayer vers les propriété de ce dernier...
    Ah vi c'est vrai, il y a le cas où il n'y a pas d'objet derrière, j'oubliais :)
    Pas de risque que ça arrive avec SpecialDeliveryDate(View|Adapter) ?
    (il manque un intellisense à la resharper pour taper les noms avec juste les initiales :)

  7. #27
    maa
    maa est déconnecté
    Membre éclairé
    Avatar de maa
    Inscrit en
    Octobre 2005
    Messages
    672
    Détails du profil
    Informations personnelles :
    Âge : 41

    Informations forums :
    Inscription : Octobre 2005
    Messages : 672
    Par défaut
    Pas de risque que ça arrive avec SpecialDeliveryDate(View|Adapter) ?
    En fait, non mais SpecialDeliveryDate(View|Adapter) à un constructeur par défaut pour que la datagrid WPF puisse créer et ajouter des instances à la collection.
    Quand on passe par ce constructeur, le model n'est pas renseigné. Est-ce qu'on peut instancier ce model dans le constructeur par défaut ?
    Ca donnerait ça :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
            private readonly SpecialDeliveryDate model;
     
            public SpecialDeliveryDateView(SpecialDeliveryDate date)
    	{
                model = date;
    	}
     
            public SpecialDeliveryDateView()
            {
                model = new SpecialDeliveryDate(avec en paramètre des valeurs par défaut que je souhaite donner aux propriétés);
            }
    L'idée que la création du model soit assuré par la vue ne me plait pas trop... Qu'en penses-tu ?

  8. #28
    Membre émérite
    Profil pro
    Inscrit en
    Septembre 2003
    Messages
    652
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2003
    Messages : 652
    Par défaut
    Citation Envoyé par maa Voir le message
    L'idée que la création du model soit assuré par la vue ne me plait pas trop... Qu'en penses-tu ?
    Mouais... je pencherais plus pour faire la même chose qu'avec DeliveryDayView en fin de compte. Pas de référence en interne à l'objet du domaine, mais tu fournis ce qu'il faut pour pouvoir le retrouver en cas de mise à jour (ou savoir qu'il faut le créer).

    Là ça commence à s'éloigner du simple "j'ai ça, je veux l'afficher, formate le comme il faut, merci" :)

  9. #29
    maa
    maa est déconnecté
    Membre éclairé
    Avatar de maa
    Inscrit en
    Octobre 2005
    Messages
    672
    Détails du profil
    Informations personnelles :
    Âge : 41

    Informations forums :
    Inscription : Octobre 2005
    Messages : 672
    Par défaut
    Ok donc j'ai implémenté INotifyPropertyChanged dans SpecialDeliveryDateView, j'ai relayé les événement à chaque changement de propriétés dans ShippingView puis j'ai écris le code suivant dans le code behind de ma Form Wpf:

    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
     
            private void Window_Loaded(object sender, RoutedEventArgs e)
            {
                IList<Shipping> shippings = _model.FindShippings();
     
                var shippingViews = new List<ShippingView>();
                foreach (var shipping in shippings)
                {
                    var shippingView = new ShippingView(shipping);
                    shippingView.DeliveryDayChanged += OnShippingViewDeliveryDayChanged;
                    shippingView.SpecialDatesChanged += OnShippingViewSpecialDatesChanged;
                    shippingView.SpecialDateChanged += OnShippingViewSpecialDateChanged;
                    shippingViews.Add(shippingView);
                }
     
                DataContext = shippingViews;
            }
     
            private void OnShippingViewSpecialDateChanged(object sender, ShippingViewEventArgs<SpecialDeliveryDateView> e)
            {
                _controller.UpdateSpecialDate(e.ShippingView, e.DeliveryView, e.PropertyName);
            }
     
            private void OnShippingViewSpecialDatesChanged(object sender, NotifyCollectionChangedEventArgs e)
            {
                _controller.UpdateSpecialDates(sender as ShippingView, e);
            }
    ...
    et dans mon contrôleur:

    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
    29
    30
    31
    32
    33
    34
    35
    36
    37
     
            public void UpdateSpecialDates(ShippingView shippingView, NotifyCollectionChangedEventArgs e)
            {
                var shipping = shippingView.Shipping;
                if(e.Action == NotifyCollectionChangedAction.Add)
                    foreach (SpecialDeliveryDateView view in e.NewItems)
                    {
                        if(shipping.FindSpecialDate(view.Date) == null)
                            shipping.AddSpecialDate(view.Date, view.NbDaysPreparations, view.Infos, view.IsDeliveryPossible);
                    }
                else if (e.Action == NotifyCollectionChangedAction.Remove)
                    foreach (SpecialDeliveryDateView view in e.OldItems)
                    {
                        var specialDateModel = shipping.FindSpecialDate(view.Date);
                        if (specialDateModel != null)
                            shipping.RemoveSpecialDate(specialDateModel);
                    }
            }
     
            public void UpdateSpecialDate(ShippingView shippingView, SpecialDeliveryDateView specialDateView, string propertyName)
            {
                var shipping = shippingView.Shipping;
                SpecialDeliveryDate specialDateModel = shipping.FindSpecialDate(specialDateView.Date);
     
                switch (propertyName)
                {
                    case ("Infos"):
                        specialDateModel.Infos = specialDateView.Infos;
                        break;
                    case ("IsDeliveryPossible"):
                        specialDateModel.IsDeliveryPossible = specialDateView.IsDeliveryPossible;
                        break;
                    case ("NbDaysPreparations"):
                        specialDateModel.NbDaysPreparations = specialDateView.NbDaysPreparations;
                        break;
                }
            }
    En résumé, pour mettre à jour les propriétés de SpecialDeliveryDateView on "fais" le chemin suivant :
    SpecialDeliveryDateView -> ShippingView -> ShippingSchedulerForm ->ShippingSchedulerController et ce dernier met enfin à jour les propriétés du modèle correspondant. Tout est ok de ce côté ?

    L'ajout est la suppression d'item est géré par le contrôleur dans la méthode UpdateSpecialDates(). Et là, j'aimerais vérifier qu'on ajoute jamais deux instances de SpecialDeliveryDate ayant la même date (d'où ma condition if(shipping.FindSpecialDate(view.Date) == null)). Dans l'idéal il faudrait supprimer la vue, puisqu'il y a déjà une vue ayant la même date et peut être renvoyer un message pour l'utilisateur. Je me pose alors plusieurs questions:
    - qui doit supprimer la l'instance SpecialDeliveryDateView en trop (ShippingSchedulerForm ou le contrôleur) ?
    - comment le contrôleur va notifier SpecialDeliveryDateView ? Par événement ?
    - je me demande enfin si ce contrôle ne devrait pas être fais plus tôt (avant que l'instance SpecialDeliveryDateView soit crée). Par exemple la vérification pourrait être faite à la levé d'un événement quand la valeur de la cellule de ma datagrid portant la date change.

  10. #30
    Membre émérite
    Profil pro
    Inscrit en
    Septembre 2003
    Messages
    652
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2003
    Messages : 652
    Par défaut
    Citation Envoyé par maa Voir le message
    - qui doit supprimer la l'instance SpecialDeliveryDateView en trop (ShippingSchedulerForm ou le contrôleur) ?
    Mmh, peut-être juste que ShippingView rafraîchisse la liste de SpecialDeliveryDateViews depuis le modèle après avoir déclenché l'évènement ? Mais pas fan... Je préfère la version d'en-dessous :

    Citation Envoyé par maa Voir le message
    - je me demande enfin si ce contrôle ne devrait pas être fais plus tôt (avant que l'instance SpecialDeliveryDateView soit crée).
    Ce serait logique en tant que validation au niveau de la présentation. Plus tôt on peut intercepter les erreurs (et savoir quoi en faire), mieux c'est. Maintenant ça n'exclut pas de vérifier du côté du modèle qu'on ne peut pas faire de doublons (après tout, c'est lui qui est responsable d'assurer l'intégrité des 'vrais' objets).

    Ça pourrait être simplement l'ajout d'une méthode sur le modèle pour savoir si une SDD particulière existe déjà que le contrôleur appellerait avant d'essayer de l'ajouter. Et le AddSDD du modèle pourrait simplement balancer une exception si le 'contrat' (pas de doublon) n'est pas respecté.

    Ce qui laisse le contrôleur décider de quoi faire quand il se retrouve avec une SDDV qui devrait virer. Là comme ça j'ai envie de dire qu'il n'a qu'à faire shippingView.RemoveSpecialDeliveryDay(day). Il a tout sous la main et l'affichage devrait se mettre à jour tout seul vu que ça repose sur du databinding.

    Citation Envoyé par maa Voir le message
    Par exemple la vérification pourrait être faite à la levé d'un événement quand la valeur de la cellule de ma datagrid portant la date change.
    C'est un peu comme la validation JS sur les pages web. Si ça permet de faire un feedback plus rapide côté utilisateur et d'éviter des traitements supplémentaires côté serveur, tant mieux. Mais ça n'empêche qu'il faut aussi spécifier le contrat là où il doit être appliqué. Donc ici, au niveau de la classe responsable de faire des ajouts.

  11. #31
    maa
    maa est déconnecté
    Membre éclairé
    Avatar de maa
    Inscrit en
    Octobre 2005
    Messages
    672
    Détails du profil
    Informations personnelles :
    Âge : 41

    Informations forums :
    Inscription : Octobre 2005
    Messages : 672
    Par défaut
    Ok donc en fait c'est le modèle et la vue qui doivent contrôler intégrité des données. Pas le contrôleur. C'est un peut étrange, je ne suis pas sûr d'avoir bien compris ce que le contrôleur contrôle...

    En fait je me demande si on ne devrait pas considérer les datagrids comme les vraie vues respectivement de Shipping, DeliveryDay et SpecialDeliveryDate (bien qu'elles soient toutes inclues dans une même form) et associer à chacune d'elle un contrôleur qui par exemple vérifie qu'il n'y a pas de doublons.

    Sinon pour synchroniser la collection SpecialDates de la vue avec celle du modèle, je me suis rendu compte que je ne peux pas me baser sur l'événement CollectionChanged de ObservableCollection<> quand un item est ajouté. En effet, une instance de SpecialDeliveryDateView est crée dès la sélection d'une nouvelle ligne dans la datagrid avant même que l'on ai pu renseigner la date. Or c'est grâce à celle-ci que je peux retrouver le model correspondant et vérifier si il y a des doublons. Donc je pense plutôt lever un événement à chaque changement de la propriété Date et ne répercuter l'ajout dans le model que lorsque la propriété est setté. Cependant je ne dois considérer l'ajout d'un item que quand Date est setté pour la première fois. Sinon c'est une simple modification de la vue (ou alors je supprime, puis je ré-ajoute un item à la collection du model). Dans tous les cas il faut que je conserve les valeurs avant et après affectation de la Date.
    Pour la suppression d'item, il faudra toujours passer par l'évènement CollectionChanged... tout ça ne simplifie pas les choses...

  12. #32
    Membre émérite
    Profil pro
    Inscrit en
    Septembre 2003
    Messages
    652
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2003
    Messages : 652
    Par défaut
    Citation Envoyé par maa Voir le message
    Ok donc en fait c'est le modèle et la vue qui doivent contrôler intégrité des données. Pas le contrôleur. C'est un peut étrange, je ne suis pas sûr d'avoir bien compris ce que le contrôleur contrôle...
    Le contrôleur gère les actions. C'est à peu près tout.
    Ce qui rajoute de la lourdeur ici, c'est que les évènements d'actions ne passent pas directement par le contrôleur. Il faut se taper la plomberie de renvoyer les évènements depuis le formulaire vers le contrôleur.

    Pour faire le rapprochement avec ASP.NET MVC, quand une action est faite sur la page web, c'est directement le contrôleur qui est appelé avec l'action correspondante. Il pilote les opérations sur le modèle, il obtient les objets nécessaires, il crée les versions adaptées pour affichage et basta.
    La vue récupère ça et fait son affichage (avec ses contrôles de validation côté client, etc).

    Il n'y a pas la partie où la vue se remet à jour automatiquement quand le modèle change, mais web oblige, une fois que la vue est affichée c'est fini, la requête suivante repartira de zéro.

    Citation Envoyé par maa Voir le message
    En fait je me demande si on ne devrait pas considérer les datagrids comme les vraie vues respectivement de Shipping, DeliveryDay et SpecialDeliveryDate (bien qu'elles soient toutes inclues dans une même form) et associer à chacune d'elle un contrôleur qui par exemple vérifie qu'il n'y a pas de doublons.
    À un niveau plus fin c'est un peu ça. C'est aussi ce dont parle Fowler quand il dit
    Citation Envoyé par Martin Fowler
    I should stress that there's not just one view and controller, you have a view-controller pair for each element of the screen, each of the controls and the screen as a whole.
    Et c'est aussi un peu ce qu'est le mécanisme de databinding lui-même. Le datagrid est la vue, la datasource est le modèle, la tuyauterie qui transmet les actions du datagrid sur la datasource est le contrôleur. Le truc étant que tu n'as pas forcément suffisamment la main dessus pour ajouter tout ce que tu veux, d'où le niveau supplémentaire pour intercaler tout ça.

    Si essayer de rassembler les traitements au niveau du formulaire et de son contrôleur est trop lourd, tu peux toujours partir sur l'option de faire un MVC (enfin un VC) pour chaque élément. Chaque contrôleur agit sur le modèle dans son coin et chaque vue surveille le modèle pour détecter quand un changement lié à une action sur un autre élément nécessite une réaction de son côté.

    Tu pourrais aussi faire un contrôleur rattaché à chaque datagrid et qui s'occupe de l'alimenter, de formater et filtrer son contenu et de retranscrire les évènements en actions sur le modèle. Et là... ben en gros tu fais du MVP au niveau de chaque datagrid :)


    Il y a plein de combinaisons possibles. Le grand jeu est de trouver celles qui collent le mieux avec ce que tu veux faire :)

  13. #33
    maa
    maa est déconnecté
    Membre éclairé
    Avatar de maa
    Inscrit en
    Octobre 2005
    Messages
    672
    Détails du profil
    Informations personnelles :
    Âge : 41

    Informations forums :
    Inscription : Octobre 2005
    Messages : 672
    Par défaut
    ok mais le problème c'est que la datagrid qui affiche les deliveryday (comme celle qui affiche les specialDeliveryDate) affiche la collection du shipping courant et donc gère l'affichage des collections de tous les shippings. Donc on a une vue liée à n collections du modèle. Faut-il alors 1 ou n contrôleur(s) ?

    Si essayer de rassembler les traitements au niveau du formulaire et de son contrôleur est trop lourd, tu peux toujours partir sur l'option de faire un MVC (enfin un VC) pour chaque élément.
    Je ne suis pas trop clair avec ça... Tu veux dire un contrôleur par ShippingView pour éviter de relayer les événements dans le formulaire ?

  14. #34
    Membre émérite
    Profil pro
    Inscrit en
    Septembre 2003
    Messages
    652
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2003
    Messages : 652
    Par défaut
    Citation Envoyé par maa Voir le message
    Donc on a une vue liée à n collections du modèle. Faut-il alors 1 ou n contrôleur(s) ?
    1 vue == 1 contrôleur.
    Le 'modèle', c'est le point d'accès à tous les éléments du domaine (Shipping, DeliveryDay, SpecialDeliveryDay, ...). Lui peut être utilisé par plusieurs couples vue/contrôleur.

    Citation Envoyé par maa Voir le message
    Je ne suis pas trop clair avec ça... Tu veux dire un contrôleur par ShippingView pour éviter de relayer les événements dans le formulaire ?
    Je veux dire (enfin je traduis de la page de Fowler) un couple vue/contrôleur par élément graphique. À toi de voir à quel niveau de granularité tu veux faire ça.

    Si le formulaire fonctionne à coups de datagrid, tu peux faire un couple vue/contrôleur par datagrid (et toujours un au niveau du formulaire lui-même). Le problème étant que contrairement au formulaire, tu n'as pas beaucoup la main sur les datagrids. Si c'est suffisant, tant mieux, sinon il te faut probablement un intermédiaire.

    Maintenant si tu as déjà un peu de mal à jongler entre un modèle et un couple vue/contrôleur, il vaut peut-être mieux éviter de multiplier les vues/contrôleurs dans l'immédiat :)

    Encore une fois, pas mal de possibilités sont détaillées sur la page de Fowler. Si tu n'as pas regardé en détail la partie sur le MVC, fais-le, parce que je ne vais pas tout retranscrire ici (et je le ferais moins bien :)

  15. #35
    maa
    maa est déconnecté
    Membre éclairé
    Avatar de maa
    Inscrit en
    Octobre 2005
    Messages
    672
    Détails du profil
    Informations personnelles :
    Âge : 41

    Informations forums :
    Inscription : Octobre 2005
    Messages : 672
    Par défaut
    Ok je vais lire en détail la documentation de Fowler.

    Merci beaucoup pour tous les conseils que tu m'a donnés. Je suis un peu gêné en regardant la longueur de notre fil de discussion... Je te remercie d'avoir pris du temps pour m'expliquer toutes ces choses.

    J'aurais une dernière question :
    L'utilisateur peut à tout moment sauvegarder (persister en base de données) en cliquant sur un bouton.
    Quand il quitte l'application, il faudrait lui demander si il veux enregistrer les modifications, mais seulement si il y a eu des modifications depuis la dernière sauvegarde.
    Pour détecter si il y a eu des modifications depuis la dernière sauvegarde, un moyen serait d'ajouter une propriété booléenne "MustSave" au contrôleur et la passer à True quand l'une des méthodes, UpdateDeliveryDay(), UpdateSpecialDates() ou UpdateSpecialDate() est appelée.
    Quand le contrôleur appelle ShippingSchedulerModel.SaveChanges() il repasse MustSave à false.
    Enfin quand on quitte le formulaire la méthode Window_Closing() de ma form wpf va vérifier si MustSave du contrôleur est à True avant de poser la question à l'utilisateur.

    Ca te parait correct que le contrôleur gère tout ça ?

  16. #36
    Membre émérite
    Profil pro
    Inscrit en
    Septembre 2003
    Messages
    652
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2003
    Messages : 652
    Par défaut
    Citation Envoyé par maa Voir le message
    Ca te parait correct que le contrôleur gère tout ça ?
    Mmh... ça me chatouille un peu que la vue doive demander des infos au contrôleur. Et évidemment, c'est en tapant "mais là comme ça je n'ai pas de meilleure idée" que ce qui suit est venu :)

    Encore et toujours basé sur la page de Fowler, la vue n'est censée être mise au courant des changements côté modèle que via un mécanisme d'observation des évènements du modèle (comme le contrôleur est au courant des actions via l'observation des évènements sur l'interface).

    Donc, puisque la vue est censée surveiller le modèle pour savoir quand il y a eu des modifs, quand le contrôleur fait des mises à jour sur le modèle, ça devrait déclencher un évènement de mise à jour que ton formulaire surveillerait. Comme ça il saurait quand des modifs ont été faites.

    Même chose avec la méthode pour persister les changements : le modèle balance un évènement disant que tout a été persisté, et hop, la vue est prévenue.

    Ça sonne comme l'approche la plus 'pure' des deux. Une action est faite sur l'interface, ça déclenche un évènement que le contrôleur surveille, il modifie le modèle, ce qui déclenche un évènement côté modèle que la (ou les) vue(s) surveille(nt) et qui se remettent à jour si besoin. Dans le cas présent, la mise à jour se limiterait à un flag pour savoir si des modifs non-persistées sont en cours ou non.

    Mettre le flag sur le contrôleur, ça le transforme partiellement en Presentation Model (cf Fowler, encore), ce qui n'est pas vraiment son rôle en MVC.

  17. #37
    maa
    maa est déconnecté
    Membre éclairé
    Avatar de maa
    Inscrit en
    Octobre 2005
    Messages
    672
    Détails du profil
    Informations personnelles :
    Âge : 41

    Informations forums :
    Inscription : Octobre 2005
    Messages : 672
    Par défaut
    Donc, puisque la vue est censée surveiller le modèle pour savoir quand il y a eu des modifs, quand le contrôleur fait des mises à jour sur le modèle, ça devrait déclencher un évènement de mise à jour que ton formulaire surveillerait. Comme ça il saurait quand des modifs ont été faites.
    Qui déclenche les évènements "ModelSaved" et "ModelModified" ? Ca ne peut pas être les éléments du modèles qui sont mis à jours (Shipping, DeliveryDay ou SpecialDeliveryDate).
    - Ca peut être le contrôleur directement (ce qui serait le plus court)
    - Ca peut être la classe ShippingSchedulerModel dont le rôle est de charger/persister les données.
    D'après ce que j'ai compris, ça serait plutôt la seconde solution la meilleure. Il faudrait alors une méthode public dans ShippingSchedulerModel pour déclencher l'événement. Mais potentiellement tout le monde pourrait l'appeler...

  18. #38
    Membre émérite
    Profil pro
    Inscrit en
    Septembre 2003
    Messages
    652
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2003
    Messages : 652
    Par défaut
    Citation Envoyé par maa Voir le message
    Qui déclenche les évènements "ModelSaved" et "ModelModified" ? Ca ne peut pas être les éléments du modèles qui sont mis à jours (Shipping, DeliveryDay ou SpecialDeliveryDate).
    - Ca peut être le contrôleur directement (ce qui serait le plus court)
    - Ca peut être la classe ShippingSchedulerModel dont le rôle est de charger/persister les données.
    D'après ce que j'ai compris, ça serait plutôt la seconde solution la meilleure. Il faudrait alors une méthode public dans ShippingSchedulerModel pour déclencher l'événement. Mais potentiellement tout le monde pourrait l'appeler...
    La vue observe les évènements du modèle, donc oui, c'est du côté du modèle que les évènements doivent se déclencher.

    Après, l'endroit exact où l'évènement est déclenché dans le modèle, c'est le grand jeu :)

    Que ces évènements soient sur ShippingSchedulerModel est de loin le plus simple pour l'observation depuis la vue (pour ce dont tu as besoin ici). Mais ça ne veut pas dire qu'il faut ajouter une méthode RaiseXxx que le contrôleur appellerait. C'est au modèle de savoir quand des modifs sont faites et quand le tout est persisté, pour déclencher l'évènement correspondant.

    Et donc ça voudrait dire soit que le contrôleur ne modifie/crée/supprime pas lui-même les objets du domaine mais transmet le nécessaire au modèle pour qu'il s'en charge, soit qu'il y a un mécanisme sur ces objets eux-mêmes pour indiquer qu'ils ont été modifiés.

    Je te laisse te triturer le cerveau pour voir ce qui est le moins lourd pour que tout ça se goupille, moi j'ai mal au crâne :)

  19. #39
    maa
    maa est déconnecté
    Membre éclairé
    Avatar de maa
    Inscrit en
    Octobre 2005
    Messages
    672
    Détails du profil
    Informations personnelles :
    Âge : 41

    Informations forums :
    Inscription : Octobre 2005
    Messages : 672
    Par défaut
    ok, merci beaucoup.

  20. #40
    maa
    maa est déconnecté
    Membre éclairé
    Avatar de maa
    Inscrit en
    Octobre 2005
    Messages
    672
    Détails du profil
    Informations personnelles :
    Âge : 41

    Informations forums :
    Inscription : Octobre 2005
    Messages : 672
    Par défaut
    J'ai pas mal réfléchi, mais il y a encore quelque chose qui me travaille...

    Pour faire le rapprochement avec ASP.NET MVC, quand une action est faite sur la page web, c'est directement le contrôleur qui est appelé avec l'action correspondante. Il pilote les opérations sur le modèle, il obtient les objets nécessaires, il crée les versions adaptées pour affichage et basta.
    La vue récupère ça et fait son affichage (avec ses contrôles de validation côté client, etc).
    Ici les versions adaptées pour l'affichage sont les shippingView avec ses collections de DeliveryDayView et SpecialDeliveryDateView. Si le contrôleur doit les créer, alors il faut mettre ce bout de code:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
                IList<Shipping> shippings = _model.FindShippings();
     
                var shippingViews = new List<ShippingView>();
                foreach (var shipping in shippings)
                {
                    var shippingView = new ShippingView(shipping);
                    shippingView.DeliveryDayChanged += OnShippingViewDeliveryDayChanged;
                    shippingView.SpecialDateRemoved += OnShippingViewSpecialDatesChanged;
                    shippingView.SpecialDateChanged += OnShippingViewSpecialDateChanged;
                    shippingViews.Add(shippingView);
                }
    dans une méthode du contrôleur et appeler directement ses méthodes quand les événements sont levés.
    Ici le truc qui est un peu bizarre c'est qu'il n'y a pas directement d'appels utilisateurs sur le contrôleur comme en ASP.net MVC. Ce sont des levés d'événements générés par la modification de propriétés ou de collections d'objets ViewModel qui jouent le rôle d'appels utilisateurs. Mais dans la même logique qu'en ASP.net MVC c'est le contrôleur qui devrait réagir à ces "appels" sans qu'on ai besoin de passer par la vue. C'est d'ailleurs assez normal que celui qui crée ces objets ViewModel soit aussi celui qui les surveille non ?
    Un seul problème se pose alors. Comme la vue ne demande plus d'action au contrôleur, elle ne peut pas catcher les erreurs qui se produiraient dans le model pour par exemple afficher des alertes à l'utilisateur. Comment alors faire remonter jusqu'à la vue des erreurs qui se produirait dans le model ? Je suppose qu'il y a le même problème en APS.net MVC; j'ai vu qu'on posait un attribut [HandleError] sur le contrôleur, mais inutilisable pour une application desktop.

Discussions similaires

  1. Quelques questions sur mon application en pattern MVC
    Par Pavel37 dans le forum Débuter
    Réponses: 0
    Dernier message: 08/03/2013, 11h13
  2. Réponses: 8
    Dernier message: 21/03/2012, 11h00
  3. Quelques questions sur les design pattern
    Par JulienDuSud dans le forum C++
    Réponses: 8
    Dernier message: 22/04/2009, 21h41
  4. Quelques question sur Win 32 Appli
    Par lvdnono dans le forum Windows
    Réponses: 5
    Dernier message: 15/06/2004, 12h37
  5. Quelques questions sur le TWebBrowser...
    Par CorO dans le forum Web & réseau
    Réponses: 3
    Dernier message: 17/01/2003, 21h23

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