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

C# Discussion :

Explications sur le pattern MVP


Sujet :

C#

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Rédacteur
    Avatar de Arnaud F.
    Homme Profil pro
    Développeur COBOL
    Inscrit en
    Août 2005
    Messages
    5 183
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : France

    Informations professionnelles :
    Activité : Développeur COBOL
    Secteur : Finance

    Informations forums :
    Inscription : Août 2005
    Messages : 5 183
    Par défaut Explications sur le pattern MVP
    Bonjour,

    dans un avenir proche, je dois développer plusieurs applications (en WinForms dans un premier temps et WPF dans un second).

    Pour mes applications WinForms, je souhaite donc utilisé le pattern MVP pour une raison très simple, c'est plusieurs UI qui utilisent le même code métier.

    Dans un premier temps, je suis sur l'application de chargement (un bête Splash Screen).

    J'ai parcouru quelques articles sur le net mais tout ça me paraît encore bien mystérieux.

    Les comportements que je souhaite définir pour l'UI du splash screen :
    • se ferme automatiquement en cas de réussite ;
    • ne se ferme pas en cas d'erreur ;
    • affiche l'état d'avancement ainsi que divers messages.


    J'ai donc mon code métier :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    // Throws *Exception
    public static class Business
    {
        public static void Update() { /* ... */ }
    }
    Mon interface à implémenter pour chaque splash screen
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    public interface ISplashScreenView
    {
        void UpdateView();
    }
    Ma classe Presenter :

    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
    public class SplashScreenPresenter
    {
            private ISplashScreenView _view;
     
            public SplashScreenPresenter(ISplashScreenView view)
            {
                _view = view;
     
                // Lancement de la mise à jour
                try
                {
                    CanClose = false;
                    Business.Update();
                }
                catch(Exception ex)
                {
                    ErrorMessage = ex.Message;
                }
                finally
                {
                    CanClose = true;
                }
            }
     
            public string CanClose 
            { get; internal set; }
     
            public string ErrorMessage 
            { get; internal set; }
     
            public string Message 
            { get; internal set; }
     
            public int Percentage
            { get; internal set; }
    }
    Une des UI :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    public partial class SplashScreen : Form, ISplashScreenView
    {
            private void SplashScreen_Load(object sender, EventArgs e)
            {
                new SplashScreenPresenter(this);
            }
     
            public void UpdateView() { /* ... */ }
    }
    Mais là je sèche, en effet, c'est mon code Business qui détient les messages à affecter et le taux d'avancement, ce qui impliquerait que je passe ma classe Presenter à ma classe Business...

    C'est bien comme ça qu'il faut procéder? Y a-t-il des failles dans mon système (sûrement )?

    Comment implémenter la gestion des events? (Dire à l'UI de quitter ou non), ...

    Le Presenter doit-il implémenter INotifyPropertyChanged?

    C'est par l'adresse que vaut le bûcheron, bien plus que par la force. Homère

    Installation de Code::Blocks sous Debian à partir de Nightly Builds

  2. #2
    Membre Expert Avatar de Guulh
    Homme Profil pro
    Inscrit en
    Septembre 2007
    Messages
    2 160
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Septembre 2007
    Messages : 2 160
    Par défaut
    Ah, le MVP J'avais bien aimé cette note de blog : http://www.ctrl-shift-b.com/2008/11/...senter-styles/ qui montre bien qu'il y a plein de façons différentes de faire, en allant à l'extrême au MVVM.

    Ce qui est fixe, en tous cas, c'est que la vue ne contient, dans la mesure du possible, que du code trivial, c'est à dire qu'elle n'expose que des propriétés publiques (du style public string Login { get { return tbLogin.Text; } }), des appels au presenter suite à l'action de l'user (du style btnClick(object s, EventArgs e) { _presenter.AuBoulot(); }), ...

    Ce qui dépend du goût de chacun, ensuite, c'est la nature du lien vue / presenter.
    • La vue lève des events quand telle ou telle action se produit, et elle peut ne pas avoir besoin du presenter (cf l'article)
    • La vue référence le presenter, et appelle directement certaines de ses méthodes
    • La vue se binde aux propriétés du presenter, dans les limites du moteur de binding utilisé
    Je préfère la seconde solution, perso, qui facilite l'écriture de tests unitaires du presenter.

    Mais là, tel que tu le fais, si ton IView n'a qu'une méthode UpdateView, ça laisse supposer que c'est la vue elle même qui va lire des propriétés du presenter pour se mettre à jour elle-même. Pas good

    Non ta vue devrait plutôt ressembler à ça :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    interface ISplashScreenView
    {
       void AddMessage(string msg);
       void ReportProgress(int percent);
       void Close();
    }
    Pour ce qui est de INotifyPropertyChanged : bah ça dépend comme tu fais ton binding. En effet, tu peux par exemple avoir le presenter qui a lui-même des propriétés auxquelles la vue s'abonnera, et là pour que le binding se mette à jour, il faut effectivement implémenter cette interface.

  3. #3
    Rédacteur
    Avatar de Arnaud F.
    Homme Profil pro
    Développeur COBOL
    Inscrit en
    Août 2005
    Messages
    5 183
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : France

    Informations professionnelles :
    Activité : Développeur COBOL
    Secteur : Finance

    Informations forums :
    Inscription : Août 2005
    Messages : 5 183
    Par défaut
    Citation Envoyé par Guulh Voir le message
    Ah, le MVP J'avais bien aimé cette note de blog : http://www.ctrl-shift-b.com/2008/11/...senter-styles/ qui montre bien qu'il y a plein de façons différentes de faire, en allant à l'extrême au MVVM.
    Je n'étais jamais tombé sur cet article, je vais donc prendre le temps de le lire. (L'anglais ne me dérangeant pas )

    Citation Envoyé par Guulh Voir le message
    Ce qui est fixe, en tous cas, c'est que la vue ne contient, dans la mesure du possible, que du code trivial, c'est à dire qu'elle n'expose que des propriétés publiques (du style public string Login { get { return tbLogin.Text; } }), des appels au presenter suite à l'action de l'user (du style btnClick(object s, EventArgs e) { _presenter.AuBoulot(); }), ...
    Je suis tout à fait d'accord, d'où l'utilité de ce pattern, car pour ce même Splash Screen je suis amené à le faire pour différents framework (Compact (CE / Mobile) & NET Framework ...)

    Citation Envoyé par Guulh Voir le message
    Ce qui dépend du goût de chacun, ensuite, c'est la nature du lien vue / presenter.
    • La vue lève des events quand telle ou telle action se produit, et elle peut ne pas avoir besoin du presenter (cf l'article)
    • La vue référence le presenter, et appelle directement certaines de ses méthodes
    • La vue se binde aux propriétés du presenter, dans les limites du moteur de binding utilisé
    Je préfère la seconde solution, perso, qui facilite l'écriture de tests unitaires du presenter.
    Je penche aussi pour le second pour la même raison à savoir que dans mon cas, nous effectuerons toujours des tests unitaires sur le business avant même de tester l'UI (secondaire).

    Citation Envoyé par Guulh Voir le message
    Mais là, tel que tu le fais, si ton IView n'a qu'une méthode UpdateView, ça laisse supposer que c'est la vue elle même qui va lire des propriétés du presenter pour se mettre à jour elle-même. Pas good
    C'est comme ça que je voyais la chose justement à savoir que la vue se met à jour en lisant les données du presenter, en quoi c'est choquant? Dans tous les cas la vue devra se mettre elle-même à jour, non? (cf dernière remarque)

    Citation Envoyé par Guulh Voir le message
    Non ta vue devrait plutôt ressembler à ça :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    interface ISplashScreenView
    {
       void AddMessage(string msg);
       void ReportProgress(int percent);
       void Close();
    }
    Soit

    Citation Envoyé par Guulh Voir le message
    Pour ce qui est de INotifyPropertyChanged : bah ça dépend comme tu fais ton binding. En effet, tu peux par exemple avoir le presenter qui a lui-même des propriétés auxquelles la vue s'abonnera, et là pour que le binding se mette à jour, il faut effectivement implémenter cette interface.
    Justement, plus haut, tu disais que c'était "Pas good" ( ) de réaliser de la sorte, à savoir que la vue s'abonne au propriétés du presenter. Et donc ... ?

    Une question reste en suspend : comment faire pour que mon code métier puisse mettre à jour les messages du Presenter !?

    m'sieur
    C'est par l'adresse que vaut le bûcheron, bien plus que par la force. Homère

    Installation de Code::Blocks sous Debian à partir de Nightly Builds

  4. #4
    Membre Expert Avatar de Guulh
    Homme Profil pro
    Inscrit en
    Septembre 2007
    Messages
    2 160
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Septembre 2007
    Messages : 2 160
    Par défaut
    Citation Envoyé par Arnaud F. Voir le message
    Je penche aussi pour le second pour la même raison à savoir que dans mon cas, nous effectuerons toujours des tests unitaires sur le business avant même de tester l'UI (secondaire).
    Oui, mais le presenter a quand même de la logique qui est testable. Par exemple, s'assurer qu'on affiche un message à l'user quand un champ login est vide, que la vue est bien mise à jour quand telle méthode est appelée, etc.


    Citation Envoyé par Arnaud F. Voir le message
    C'est comme ça que je voyais la chose justement à savoir que la vue se met à jour en lisant les données du presenter, en quoi c'est choquant? Dans tous les cas la vue devra se mettre elle-même à jour, non? (cf dernière remarque)
    Si elle ne se met à jour que via binding, pas de souci. Si on fait confiance au moteur de binding, y'a pas de place pour un bug. Par contre, une méthode du style (au pif)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    if (Today = Mardi)
       this.BackColor = Color.Blue;
    // ou bien
    if (this.Presenter.Model.DownloadFinished)
      this.tbMessage.Text += ""ayé, j'ai fini !"
    (c'est à dire du binding manuel) a sa place dans le presenter, la vue se contentant de wrapper BackColor et tbMessage dans des propriétés / des méthodes.

    Une question reste en suspend : comment faire pour que mon code métier puisse mettre à jour les messages du Presenter !?
    Ben disons que ton modèle a cette tête :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    class Modele
    {
       public void CommencerTraitementTrèsTrèsLong() { ... }
       public event MachinEventHandler Tonevent;
    }
    Le presenter, à un moment, lance le traitement du modèle; il est abonné à l'event, et l'event contient notamment un message à afficher. La tâche du Presenter se limite ici, dans la méthode abonnée à l'event, d'appeler la méthode qui va bien de la vue (AddNewMessage, par exemple), et la vue, fait e qu'elle veut du message (le mettre dans une ListBox, la prononcer à haute voix traduite en klingon, ...)

    Par ailleurs, il est préférable d'éviter que ta classe Business soit statique, sinon tes presenters ne seront pas testables.


    Je résume: la mise à jour de la vue fait partie des choses que l'on veut rendre testable. Ca peut se faire soit en mettant la logique de MAJ dans le presenter (qui est là pour ça), soit en rendant cette MAJ triviale par l'usage de binding (le test se limitant alors à tester la valeur de telle ou telle propriété du presenter).

  5. #5
    Rédacteur
    Avatar de Arnaud F.
    Homme Profil pro
    Développeur COBOL
    Inscrit en
    Août 2005
    Messages
    5 183
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : France

    Informations professionnelles :
    Activité : Développeur COBOL
    Secteur : Finance

    Informations forums :
    Inscription : Août 2005
    Messages : 5 183
    Par défaut
    Citation Envoyé par Guulh Voir le message
    Oui, mais le presenter a quand même de la logique qui est testable. Par exemple, s'assurer qu'on affiche un message à l'user quand un champ login est vide, que la vue est bien mise à jour quand telle méthode est appelée, etc.
    Oui tout à fait, les TU c'est aussi pour le Presenter, désolé de pas l'avoir écrit


    Citation Envoyé par Guulh Voir le message
    Si elle ne se met à jour que via binding, pas de souci. Si on fait confiance au moteur de binding, y'a pas de place pour un bug. Par contre, une méthode du style (au pif) [...] (c'est à dire du binding manuel) a sa place dans le presenter, la vue se contentant de wrapper BackColor et tbMessage dans des propriétés / des méthodes.
    Je suis d'accord. Mon but étant quand même de limiter la vue au maximum, beaucoup de choses seront dans le Presenter et la mise à disposition des messages étant déjà dans le Presenter, la vue n'aura qu'a récupérer les messages en question.

    Je souhaite (pour le splash screen tout du moins) limiter au maximum la liaison Presenter -> Vue (un simple pattern Observable au final).

    Citation Envoyé par Guulh Voir le message
    Ben disons que ton modèle a cette tête :
    [ ... ]
    Le presenter, à un moment, lance le traitement du modèle; il est abonné à l'event, et l'event contient notamment un message à afficher. La tâche du Presenter se limite ici, dans la méthode abonnée à l'event, d'appeler la méthode qui va bien de la vue (AddNewMessage, par exemple), et la vue, fait e qu'elle veut du message (le mettre dans une ListBox, la prononcer à haute voix traduite en klingon, ...)
    Ok, le Presenter n'est qu'un relai au final

    Citation Envoyé par Guulh Voir le message
    Par ailleurs, il est préférable d'éviter que ta classe Business soit statique, sinon tes presenters ne seront pas testables.
    Ah bon, pourquoi !?

    Citation Envoyé par Guulh Voir le message
    Je résume: la mise à jour de la vue fait partie des choses que l'on veut rendre testable. Ca peut se faire soit en mettant la logique de MAJ dans le presenter (qui est là pour ça), soit en rendant cette MAJ triviale par l'usage de binding (le test se limitant alors à tester la valeur de telle ou telle propriété du presenter).
    C'est par l'adresse que vaut le bûcheron, bien plus que par la force. Homère

    Installation de Code::Blocks sous Debian à partir de Nightly Builds

  6. #6
    Membre Expert Avatar de Guulh
    Homme Profil pro
    Inscrit en
    Septembre 2007
    Messages
    2 160
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Septembre 2007
    Messages : 2 160
    Par défaut
    Citation Envoyé par Arnaud F. Voir le message
    Je souhaite (pour le splash screen tout du moins) limiter au maximum la liaison Presenter -> Vue (un simple pattern Observable au final).
    Tu veux que qui observe qui ? Si tu fais du binding, alors la vue observera le presenter, mais ça sera automatique. Sinon, ça veut dire que la vue aura l'intelligence de savoir quoi faire des événements émis par le presenter, alors qu'on veut précisément qu'elle soit la plus stupide possible. Et dans l'autre sens, a priori les seuls events que la vue peut émettre sont liées à l'activité de l'utilisateur, et comme on a vu tout à l'heure on a le choix entre (1) la vue appelle alors des méthodes du presenter ou (2) elle émet un event, et charge au presenter de s'y abonner. C'est juste une question de goût. Comme je disais, je préfère la deuxième solution pour faciliter les tests, mais aussi parce que je trouve que ce'st de l'overkill d'utiliser les events alors qu'on a qu'un abonné, bien connu de surcroît.
    Citation Envoyé par Arnaud F. Voir le message
    Ah bon, pourquoi !?
    Pourquoi pas statique ? Ben parce que tu pourras pas le mocker. Ton presenter aura forcément un lien explicite vers ta classe business (pas le choix, ses méthodes sont statiques), et tu pourras pas isoler les méthodes qui font appel au Business.

    Accessoirement, en relisant ton post initial : il n'est pas nécessaire de faire du MVP ou du MVC pour avoir du code réutilisable. Si l'appli est déjà correctement séparée en couches, plusieurs UI pourront attaquer le même code métier. Après, là où tu peux gagner avec le MVP, c'est si tes différentes UI partagent beaucoup de logique de présentation, auquel cas tu peux espérer réutiliser aussi les Presenters. et ça facilitera aussi une migration vers WPF

Discussions similaires

  1. [Multimédia] Explication sur stream audio
    Par champion dans le forum Développement
    Réponses: 1
    Dernier message: 20/01/2005, 12h14
  2. Besoin d'explications sur float et l'élasticité !
    Par KneXtasY dans le forum Mise en page CSS
    Réponses: 2
    Dernier message: 14/01/2005, 15h15
  3. s.v.p :explication sur le ".h" et dll de l'opengl
    Par Asmod_D dans le forum OpenGL
    Réponses: 1
    Dernier message: 22/11/2004, 10h32
  4. Réponses: 28
    Dernier message: 18/08/2003, 11h54
  5. recherches des cours ou des explications sur les algorithmes
    Par Marcus2211 dans le forum Algorithmes et structures de données
    Réponses: 6
    Dernier message: 19/05/2002, 22h18

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