MVVM est-il inutile? [fermé]

91

La mise en œuvre orthodoxe de MVVM est-elle inutile? Je crée une nouvelle application et j'ai envisagé Windows Forms et WPF. J'ai choisi WPF car il est à l'épreuve du temps et offre beaucoup de flexibilité. Il y a moins de code et il est plus facile d'apporter des modifications importantes à votre interface utilisateur à l'aide de XAML.

Étant donné que le choix de WPF est évident, j'ai pensé que je pourrais aussi bien aller jusqu'au bout en utilisant MVVM comme architecture d'application car il offre une capacité de fusion, des problèmes de séparation et une testabilité unitaire. Théoriquement, cela semble beau comme le Saint Graal de la programmation d'interface utilisateur. Cette brève aventure; cependant, est devenu un véritable casse-tête. Comme prévu dans la pratique, je constate que j'ai échangé un problème contre un autre. J'ai tendance à être un programmeur obsessionnel dans la mesure où je veux faire les choses de la bonne manière afin d'obtenir les bons résultats et éventuellement devenir un meilleur programmeur. Le modèle MVVM vient de faire échouer mon test de productivité et vient de se transformer en un gros hack dégueulasse!

Le cas clair est l'ajout de la prise en charge d'une boîte de dialogue modale. La méthode correcte consiste à créer une boîte de dialogue et à la lier à un modèle de vue. Faire fonctionner cela est difficile. Pour bénéficier du modèle MVVM, vous devez distribuer le code à plusieurs endroits dans les couches de votre application. Vous devez également utiliser des constructions de programmation ésotériques telles que des modèles et des expressions lamba. Des trucs qui vous font regarder l'écran en vous grattant la tête. Cela fait de la maintenance et du débogage un cauchemar qui attend de se produire, comme je l'ai récemment découvert. J'avais une boîte à propos qui fonctionnait bien jusqu'à ce que j'obtienne une exception la deuxième fois que je l'ai invoquée, en disant qu'elle ne pouvait plus afficher la boîte de dialogue une fois qu'elle était fermée. J'ai dû ajouter un gestionnaire d'événements pour la fonctionnalité de fermeture à la fenêtre de dialogue, un autre dans l'implémentation IDialogView de celui-ci et enfin un autre dans IDialogViewModel. Je pensais que MVVM nous sauverait d'un piratage aussi extravagant!

Il existe plusieurs personnes avec des solutions concurrentes à ce problème et ce sont tous des hacks et ne fournissent pas une solution propre, facilement réutilisable et élégante. La plupart des boîtes à outils MVVM passent sous silence les boîtes de dialogue et lorsqu'elles y répondent, ce ne sont que des boîtes d'alerte qui ne nécessitent pas d'interfaces personnalisées ou de modèles d'affichage.

Je prévois d'abandonner le modèle de vue MVVM, du moins son implémentation orthodoxe. Qu'est-ce que tu penses? Cela a-t-il valu la peine pour vous si vous en aviez? Suis-je juste un programmeur incompétent ou MVVM n'est-il pas ce qu'il veut être?

Joël Rodgers
la source
6
Je me suis toujours demandé si MVVM était une sur-ingénierie. Question interessante.
Taylor Leese
11
Les modèles comme MVVM et MVC semblent être une sur-ingénierie, jusqu'à ce que vous deviez effectuer des modifications ou changer un composant. La première fois que vous devez faire cela, toute la cérémonie se paie d'elle-même.
Robert Harvey
41
Les lambdas sont-ils ésotériques? nouvelles pour moi.
Ray Booysen le
5
@Ray - Haha, +1 pour ce commentaire! : D
Venemo
7
Comme Alan Cooper l'a souligné il y a plus de dix ans dans About Face , si vous concevez des interfaces utilisateur et que les boîtes de dialogue modales ne sont pas un cas extrême, vous faites probablement quelque chose de mal.
Robert Rossney

Réponses:

61

Désolé si ma réponse est devenue un peu longue, mais ne me blâmez pas! Votre question est également longue.

En résumé, MVVM n'est pas inutile.

Le cas clair est l'ajout de la prise en charge d'une boîte de dialogue modale. La méthode correcte consiste à créer une boîte de dialogue et à la lier à un modèle de vue. Faire fonctionner cela est difficile.

Oui, c'est vraiment le cas.
Cependant, MVVM vous permet de séparer l'apparence de l'interface utilisateur de ses logiques. Personne ne vous oblige à l'utiliser partout, et personne ne tient un pistolet contre votre front pour vous faire créer un ViewModel distinct pour tout.

Voici ma solution pour cet exemple particulier: la
façon dont l'interface utilisateur gère une certaine entrée n'est pas l'affaire de ViewModel. J'ajouterais du code au fichier .xaml.cs de View, qui instancie la boîte de dialogue et définit la même instance de ViewModel (ou autre chose, si nécessaire) que son DataContext.

Pour bénéficier du modèle MVVM, vous devez distribuer le code à plusieurs endroits dans les couches de votre application. Vous devez également utiliser des constructions de programmation ésotériques telles que des modèles et des expressions lamba.

Eh bien, vous n'êtes pas obligé de l'utiliser à plusieurs endroits. Voici comment je le résoudrais:

  • Ajoutez le XAML à la vue, et rien dans le .xaml.cs
  • Écrivez chaque logique d'application (à l'exception de ce qui fonctionnerait directement avec les éléments de l'interface utilisateur) dans un ViewModel
  • Tout le code qui devrait être fait par l'interface utilisateur mais qui n'a rien à voir avec la logique métier va dans les fichiers .xaml.cs

Je pense que le but de MVVM est avant tout de séparer la logique de l'application et l'interface utilisateur concrète, permettant ainsi des modifications faciles (ou un remplacement complet) de l'interface utilisateur.
J'utilise le principe suivant: la vue peut connaître et assumer tout ce qu'elle veut du ViewModel, mais le ViewModel ne peut rien savoir de la vue.
WPF fournit un joli modèle de liaison que vous pouvez utiliser pour y parvenir.

(BTW, les modèles et les expressions lambda ne sont pas ésotériques s'ils sont utilisés correctement. Mais si vous ne le souhaitez pas, ne les utilisez pas.)

Des trucs qui vous font regarder l'écran en vous grattant la tête.

Ouais, je connais le sentiment. Exactement ce que je ressentais quand j'ai vu MVVM pour la première fois. Mais une fois que vous aurez compris, cela ne vous fera plus de mal.

J'avais une boîte à propos qui fonctionnait bien ...

Pourquoi mettriez-vous un ViewModel derrière une boîte à propos? Cela ne sert à rien.

La plupart des boîtes à outils MVVM passent sous silence les boîtes de dialogue et lorsqu'elles y répondent, ce ne sont que des boîtes d'alerte qui ne nécessitent pas d'interfaces personnalisées ou de modèles d'affichage.

Oui, parce que le fait même qu'un élément de l'interface utilisateur soit dans la même fenêtre, ou dans une autre fenêtre, ou soit en orbite autour de Mars pour le moment n'est pas la préoccupation des ViewModels.
Séparation des préoccupations

ÉDITER:

Voici une très belle vidéo dont le titre est Construisez votre propre framework MVVM . Ça vaut le coup de regarder.

Venemo
la source
2
+1 pour les trois derniers mots. Mais le reste de la réponse est également bon. :)
Robert Harvey
12
+1 pour les conseils sur l'utilisation du code-behind. C'est une idée fausse courante selon laquelle il est "mauvais" d'utiliser le code-behind dans MVVM ... mais pour les choses purement liées à l'interface utilisateur, c'est la voie à suivre.
Thomas Levesque
@Thomas: Ouais, je ne pourrais pas être plus d'accord. J'ai vu plusieurs implémentations où les gens mettent tout le code (même lié à l'interface utilisateur) dans le ViewModel, car (selon eux) "c'est là que se trouve le code". C'était assez piraté.
Venemo
3
@Venemo, je pense que vous pouvez encapsuler beaucoup de choses que vous voudriez mettre dans le code-behind en utilisant des techniques comme les comportements personnalisés, ce qui est pratique si vous vous retrouvez à écrire à plusieurs reprises du code glu. En général cependant, je pense qu'il est préférable d'utiliser le code-behind pour la colle que de pirater ensemble du XAML maladroit. La principale préoccupation, dans mon esprit, est de s'assurer qu'il n'y a rien dans le code derrière assez sophistiqué pour justifier des tests unitaires. Tout ce qui est suffisamment complexe est mieux encapsulé dans le ViewModel ou une classe d'extension, comme un Behavior ou MarkupExtension.
Dan Bryant
7
@ Thomas: Vous avez raison, le plus grand mythe sur MVVM est que le but de MVVM est de se débarrasser du code-behind. Le but est de sortir l'ocde non-UI du code derrière. Mettre du code de l'interface utilisateur uniquement dans le ViewModel est tout aussi mauvais que de placer le code de domaine problématique dans le code-behind.
Jim Reineri
8

Faire fonctionner cela est difficile. Pour bénéficier du modèle MVVM, vous devez distribuer le code à plusieurs endroits dans les couches de votre application. Vous devez également utiliser des constructions de programmation ésotériques telles que des modèles et des expressions lamba.

Pour une boîte de dialogue modale courante? Vous faites certainement quelque chose de mal là-bas - l'implémentation MVVM n'a pas à être si complexe.

Étant donné que vous êtes nouveau à la fois dans MVVM et WPF, il est probable que vous utilisiez des solutions sous-optimales partout et que vous compliquiez inutilement les choses - du moins je l'ai fait lorsque je suis passé à WPF. Assurez-vous que le problème est bien MVVM et non votre implémentation avant d'abandonner.

MVVM, MVC, Document-View, etc. est une vieille famille de modèles. Il y a des inconvénients, mais pas de défauts fatals du type que vous décrivez.

ima
la source
5

Je suis au milieu d'un développement MVVM assez complexe utilisant PRISM donc j'ai déjà dû faire face à ce genre de soucis.

Mes conclusions personnelles:

MVVM vs MVC / PopUps & co

  • MVVM est vraiment un excellent modèle et dans la plupart des cas, il remplace complètement MVC grâce à la puissante liaison de données dans WPF
  • L'appel de votre couche de service directement depuis le présentateur est une implémentation légitime dans la plupart des cas
  • Même des scénarios List / Detail assez complexes peuvent être implémentés par MVVM pur grâce à la syntaxe {Binding Path = /}
  • Néanmoins, lorsqu'une coordination complexe entre plusieurs vues doit être mise en œuvre, un contrôleur
  • Des événements peuvent être utilisés; l'ancien modèle qui implique le stockage des instances IView (ou AbstractObserver) dans le contrôleur est obsolète
  • Le contrôleur peut être injecté dans chaque présentateur par conteneur IOC
  • Le service IEventAggregator de Prism est une autre solution possible si la seule utilisation du contrôleur est la distribution d'événements (dans ce cas, il peut remplacer complètement le contrôleur)
  • Si les vues doivent être créées dynamiquement, c'est un travail très bien adapté pour le contrôleur (en prisme, le contrôleur recevra un IRegionManager injecté (IOC))
  • Les boîtes de dialogue modales sont pour la plupart obsolètes dans les applications composites modernes, à l'exception des opérations vraiment bloquantes comme les confirmations obligatoires; dans ces cas, l'activation modale peut être abstraite en tant que service appelé à l'intérieur du contrôleur et implémentée par une classe spécialisée, ce qui permet également des tests unitaires avancés au niveau de la présentation. Le contrôleur appellera, par exemple, IConfirmationService.RequestConfirmation («êtes-vous sûr») qui déclenchera un affichage de boîte de dialogue modale au moment de l'exécution et peut être facilement simulé pendant les tests unitaires
dan ionescu
la source
5

Je gère le problème des dialogues en trichant. My MainWindow implémente une interface IWindowServices qui expose toutes les boîtes de dialogue spécifiques à l'application. Mes autres ViewModels peuvent ensuite importer l'interface des services (j'utilise MEF, mais vous pouvez facilement passer l'interface manuellement par les constructeurs) et l'utiliser pour accomplir ce qui est nécessaire. Par exemple, voici à quoi ressemble l'interface pour une de mes petites applications utilitaires:

//Wrapper interface for dialog functionality to allow for mocking during tests
public interface IWindowServices
{
    bool ExecuteNewProject(NewProjectViewModel model);

    bool ExecuteImportSymbols(ImportSymbolsViewModel model);

    bool ExecuteOpenDialog(OpenFileDialog dialog);

    bool ExecuteSaveDialog(SaveFileDialog dialog);

    bool ExecuteWarningConfirmation(string text, string caption);

    void ExitApplication();
}

Cela met toutes les exécutions de Dialog en un seul endroit et il peut être facilement remplacé pour les tests unitaires. Je suis le modèle que le client de la boîte de dialogue doit créer pour créer le ViewModel approprié, qu'il peut ensuite configurer selon les besoins. L'appel Execute bloque et le client peut ensuite consulter le contenu du ViewModel pour voir les résultats de la boîte de dialogue.

Une conception MVVM plus `` pure '' peut être importante pour une grande application, où vous avez besoin d'une isolation plus propre et d'une composition plus complexe, mais pour les applications de petite à moyenne taille, je pense qu'une approche pratique, avec des services appropriés pour exposer les crochets requis, est tout à fait suffisante .

Dan Bryant
la source
Mais où appelez-vous cela? Ne serait-il pas préférable de simplement construire la boîte de dialogue dans les fonctions de la classe IWindowServices plutôt que de la faire passer. De cette façon, la vue du modèle appelant n'aurait rien à savoir sur l'implémentation de la boîte de dialogue particulière.
Joel Rodgers
L'interface est injectée dans n'importe laquelle de mes instances ViewModel qui a besoin d'accéder aux boîtes de dialogue de l'application. Dans la plupart des cas, je passe le ViewModel pour la boîte de dialogue, mais je suis devenu un peu paresseux et j'ai utilisé WPF OpenFileDialog et SaveFileDialog pour les appels de boîte de dialogue de fichier. Mon objectif principal était l'isolement à des fins de test unitaire, c'est donc suffisant pour atteindre cet objectif. Si vous vouliez une meilleure isolation, vous voudriez probablement créer un OpenFileViewModel et SaveFileViewModel, qui dupliqueraient les propriétés nécessaires pour les boîtes de dialogue.
Dan Bryant
Notez que ce n'est certainement pas une approche pure, dans la mesure où le ViewModel qui utilise les boîtes de dialogue connaît le ViewModel spécifique pour chaque boîte de dialogue qu'il souhaite ouvrir. Je pense que c'est assez propre, mais vous pouvez toujours ajouter une couche supplémentaire d'isolation avec une classe qui expose uniquement les paramètres requis pour l'utilisation de la boîte de dialogue, masquant toute visibilité inutile des propriétés ViewModel utilisées lors de la liaison. Pour les petites applications, je pense que cette isolation supplémentaire est excessive.
Dan Bryant
5

Les modèles de conception sont là pour vous aider, pas pour vous empêcher. Une petite partie d'être un bon développeur est de savoir quand «enfreindre les règles». Si MVVM est encombrant pour une tâche et que vous avez déterminé que la valeur future n'en vaut pas la peine, n'utilisez pas le modèle. Par exemple, comme d'autres affiches l'ont commenté, pourquoi passeriez-vous par tous les frais généraux pour mettre en œuvre une simple boîte à propos?

Les modèles de conception n'ont jamais été conçus pour être suivis de manière dogmatique.

RMart
la source
2
Correct. Une simple boîte à propos devrait être simple, mais que se passe-t-il si vous devez afficher des informations telles que la version, les licences, le processus en cours d'exécution, le nom de l'entreprise, etc. Toutes ces informations se trouvent quelque part. Dans les formulaires standard, vous pouvez simplement lier toutes ces informations et en finir avec elles. MVVM dit que vous devez créer un modèle de vue pour celui-ci, qui est redondant.
ATL_DEV
1

Comme le modèle lui-même, MVVM est génial. Mais la bibliothèque de contrôle de WPF livrée avec la prise en charge de la liaison de données NET 4.0 est très limitée, elle est bien meilleure que WinForm, mais ce n'est toujours pas suffisant pour MVVM pouvant être lié, je dirais que sa puissance représente environ 30% de ce qui est nécessaire pour MVVM pouvant être lié.
MVVM pouvant être lié: c'est l'interface utilisateur où ViewModel est câblé avec View uniquement en utilisant la liaison de données.
Le modèle MVVM concerne la représentation d'objet de ViewState, il ne décrit pas comment vous maintenez la synchronisation entre View et ViewModel, dans WPF, c'est la liaison de données, mais cela peut être n'importe quoi. Et en fait, vous pouvez utiliser le modèle MVVM dans n'importe quelle boîte à outils d'interface utilisateur qui prend en charge les événements \ callbacks, vous pouvez l'utiliser dans WinAPI pur dans WinForms (je l'ai fait, et cela ne fonctionne pas beaucoup plus avec les événements \ callbacks), et vous pouvez même l'utiliser dans Text Console, comme réécrire Norton Commander de DoS à l'aide du modèle MVVM.

En bref: MVVM n'est pas inutile, c'est génial. La bibliothèque de contrôle de NET 4.0 WPF est une poubelle.

Voici la simple preuve de concept ViewModel que vous ne pouvez pas lier de données de manière pure MVVM à l'aide de WPF.

public class PersonsViewModel
{
    public IList<Person> PersonList;
    public IList<ColumnDescription> TableColumns;
    public IList<Person> SelectedPersons;
    public Person ActivePerson;
    public ColumnDescription SortedColumn;
}

Vous ne pouvez pas lier les données des en-têtes de colonne DataGrid de WPF, vous ne pouvez pas lier les données des lignes sélectionnées, etc. Vous ne pouvez qu'imaginer comment les choses s'aggravent avec des ViewModels complexes.
La réponse est donc simple à moins que vous n'écriviez une application Hello World, l'utilisation de MVVM pouvant être lié dans WPF est inutile. Vous passerez la plupart de votre temps à réfléchir sur le hack pour vous lier ViewModel. La liaison de données est agréable, mais soyez prêt à revenir aux 70% du temps de l'événement.

Alex Burtsev
la source
Vous pouvez lier ceci, avec des convertisseurs, à un DataGrid.
Cameron MacFarland
@CameronMacFarland: Pas tous, certaines propriétés sont en lecture seule et non indexables, certaines n'existent tout simplement pas et il n'y a que des événements qui signalent un changement d'état.
Alex Burtsev le
J'admets que je n'ai pas beaucoup d'expérience avec WPF DataGrid. J'ai tendance à l'éviter car c'est moche et ne correspond plus à WPF. Cela dit, une combinaison de convertisseurs et de AttachedProperties pour gérer les événements devrait vous apporter ce dont vous avez besoin.
Cameron MacFarland
1
Alex, les problèmes que vous rencontrez concernent la conception du DataGrid, pas MVVM. Il est tout simplement incorrect de dire que «la liaison de données est agréable, mais soyez prêt à revenir aux 70% du temps de l'événement». J'ai écrit des applications WPF objectivement énormes dans lesquelles il n'y a aucun gestionnaire d'événements dans l'interface utilisateur - à l'exception du gestionnaire d'événements dont la grille de données (Telerik) a besoin pour l'initialisation.
Robert Rossney
3
Je pense que vous pourriez avoir plus de succès si, au lieu d'adopter l'attitude, "Ceci est mal conçu et ne fonctionne pas", vous avez essayé, "Pourquoi est-ce que cela fonctionne pour d'autres personnes mais pas pour moi?" Vous constaterez peut-être que la raison pour laquelle les choses sont difficiles à faire est que vous ne savez pas encore comment les faire.
Robert Rossney
0

Non, ce n'est pas inutile, mais il est difficile de comprendre même si le motif lui-même est ridiculement simple. Il y a des tonnes de désinformation là-bas et divers groupes qui se battent pour la bonne manière. Je pense qu'avec WPF et Silverlight, vous devriez utiliser MVVM ou vous serez en train de trop coder et d'essayer de résoudre des problèmes dans un nouveau modèle, la méthodologie «ancienne» des formulaires gagnants qui ne fait que vous causer des ennuis. C'est plus le cas dans Silverlight car tout doit être asynchrone (des hacks autour de cela sont possibles, mais vous devez simplement choisir une autre plate-forme).

Je suggère de lire cet article Simplifier l'arborescence WPF en utilisant le modèle ViewModel avec soin pour voir comment MVVM peut être bien implémenté et vous permettre de changer votre mentalité de formulaires gagnants pour la nouvelle façon de penser dans MVVM. En bref, lorsque vous voulez faire quelque chose, appliquez d'abord la logique au ViewModel et non à la vue. Vous souhaitez sélectionner un élément? Changer une icône? Ne parcourez pas les éléments de l'interface utilisateur, mettez simplement à jour les propriétés des modèles et laissez la liaison de données faire le plus gros.

RemorqueurCapitaine
la source
-1

J'ai vu le même problème avec de nombreuses implémentations MVVM en ce qui concerne les dialogues (modaux). Quand je regarde les participants du MVVM Pattern, j'ai le sentiment qu'il manque quelque chose pour construire une application cohérente.

  • View contient les contrôles GUI spécifiques et définit l'apparence de l'interface utilisateur.
  • ViewModel représente l'état et le comportement de la présentation.
  • Le modèle peut être un objet métier de la couche domaine ou un service qui fournit les données nécessaires.

Mais il manque:

  • Qui crée les ViewModels?
  • Qui est responsable du flux de travail de l'application?
  • Qui sert d'intermédiaire entre les ViewModels lorsqu'ils ont besoin de communiquer entre eux?

Mon approche consiste à introduire un contrôleur (de cas d'utilisation) qui est responsable des points manquants. Comment cela fonctionne peut être vu dans les exemples d'applications WPF Application Framework (WAF) .

jbe
la source
Le modèle Mediator mis en œuvre par Josh Smith a résolu tous mes problèmes de communication View Model. Messenger.NotifyColleagues a fourni un moyen d'avoir des modèles de vue complètement indépendants qui savaient comment répondre aux événements mondiaux (s'ils s'en soucient) sans que deux modèles de vue se connaissent. Il a déjà sauvé notre bacon plusieurs fois maintenant.
JasonD