Je m'excuse pour la longue question, elle se lit un peu comme une diatribe, mais je promets que ce n'est pas! J'ai résumé ma (mes) question (s) ci-dessous
Dans le monde MVC, les choses sont simples. Le modèle a un état, la vue montre le modèle et le contrôleur fait des choses avec / avec le modèle (en gros), un contrôleur n'a pas d'état. Pour faire des choses, le contrôleur a des dépendances sur les services Web, le référentiel, le lot. Lorsque vous instanciez un contrôleur, vous vous souciez de fournir ces dépendances, rien d'autre. Lorsque vous exécutez une action (méthode sur Controller), vous utilisez ces dépendances pour récupérer ou mettre à jour le modèle ou appeler un autre service de domaine. S'il y a un contexte, par exemple, comme un utilisateur souhaite voir les détails d'un élément particulier, vous passez l'ID de cet élément comme paramètre à l'action. Nulle part dans le contrôleur il n'y a de référence à un état. Jusqu'ici tout va bien.
Entrez MVVM. J'adore WPF, j'adore la liaison de données. J'adore les frameworks qui rendent la liaison de données à ViewModels encore plus facile (en utilisant Caliburn Micro atm). Je pense cependant que les choses sont moins simples dans ce monde. Faisons l'exercice à nouveau: le modèle a l' état, la vue montre la ViewModel et le ViewModel fait des choses à / avec le modèle (essentiellement), un ViewModel n'ont l' état! (à préciser, peut - être il délègue toutes les propriétés à un ou plusieurs modèles, mais cela signifie qu'il doit avoir une référence au modèle d' une façon ou d'une autre, ce qui est l' état en soi) Pour fairele ViewModel a des dépendances sur les services Web, le référentiel, le lot. Lorsque vous instanciez un ViewModel, vous vous souciez de fournir ces dépendances, mais aussi l'état. Et cela, mesdames et messieurs, m'agace sans fin.
Chaque fois que vous avez besoin d'instancier un à ProductDetailsViewModel
partir du ProductSearchViewModel
(dont vous avez appelé le ProductSearchWebService
qui à son tour est revenu IEnumerable<ProductDTO>
, tout le monde est toujours avec moi?), Vous pouvez faire l'une des choses suivantes:
- appel
new ProductDetailsViewModel(productDTO, _shoppingCartWebService /* dependcy */);
, c'est mauvais, imaginez 3 autres dépendances, cela signifie que vousProductSearchViewModel
devez également assumer ces dépendances. Changer le constructeur est également douloureux. - appel
_myInjectedProductDetailsViewModelFactory.Create().Initialize(productDTO);
, l'usine n'est qu'un Func, ils sont facilement générés par la plupart des frameworks IoC. Je pense que c'est mauvais parce que les méthodes Init sont une abstraction qui fuit. Vous ne pouvez pas non plus utiliser le mot clé readonly pour les champs définis dans la méthode Init. Je suis sûr qu'il y a quelques autres raisons. - appelez
_myInjectedProductDetailsViewModelAbstractFactory.Create(productDTO);
donc ... c'est le modèle (fabrique abstraite) qui est généralement recommandé pour ce type de problème. Je pensais que c'était génial car il satisfait mon envie de frappe statique, jusqu'à ce que je commence à l'utiliser. Je pense que la quantité de code passe-partout est excessive (vous savez, à part les noms de variables ridicules que j'utilise). Pour chaque ViewModel qui nécessite des paramètres d'exécution, vous obtiendrez deux fichiers supplémentaires (interface d'usine et implémentation), et vous devrez taper les dépendances non liées à l'exécution comme 4 fois supplémentaires. Et chaque fois que les dépendances changent, vous pouvez également les changer en usine. J'ai l'impression que je n'utilise même plus de conteneur DI. (Je pense que Castle Windsor a une sorte de solution pour cela [avec ses propres inconvénients, corrigez-moi si je me trompe]). - faire quelque chose avec des types anonymes ou un dictionnaire. J'aime ma saisie statique.
Donc voilà. Mélanger l'état et le comportement de cette manière crée un problème qui n'existe pas du tout dans MVC. Et j'ai l'impression qu'il n'y a actuellement pas de solution vraiment adéquate à ce problème. J'aimerais maintenant observer certaines choses:
- Les gens utilisent réellement MVVM. Donc, soit ils ne se soucient pas de tout ce qui précède, soit ils ont une autre brillante solution.
- Je n'ai pas trouvé d'exemple détaillé de MVVM avec WPF. Par exemple, l'exemple de projet NDDD m'a énormément aidé à comprendre certains concepts DDD. J'aimerais vraiment que quelqu'un me pointe vers quelque chose de similaire pour MVVM / WPF.
- Peut-être que je fais mal MVVM et que je devrais renverser ma conception. Peut-être que je ne devrais pas avoir ce problème du tout. Eh bien, je sais que d'autres personnes ont posé la même question, donc je pense que je ne suis pas le seul.
Résumer
- Ai-je raison de conclure que le fait que le ViewModel soit un point d'intégration pour l'état et le comportement est la raison de certaines difficultés avec le modèle MVVM dans son ensemble?
- L'utilisation du modèle d'usine abstrait est-elle la seule / meilleure façon d'instancier un ViewModel de manière statique?
- Existe-t-il quelque chose comme une implémentation de référence approfondie disponible?
- Est-ce que le fait d'avoir beaucoup de ViewModels avec un état / comportement est une odeur de conception?
Réponses:
Le problème des dépendances lors du lancement d'un nouveau modèle de vue peut être traité avec IOC.
Lors de la mise en place du conteneur ...
Lorsque vous avez besoin de votre modèle de vue:
Lors de l'utilisation d'un cadre tel que caliburn micro, il existe souvent une certaine forme de conteneur IOC déjà présent.
la source
Je travaille quotidiennement avec ASP.NET MVC et travaille sur un WPF depuis plus d'un an et voici comment je le vois:
MVC
Le contrôleur est censé orchestrer des actions (récupérer ceci, ajouter cela).
La vue est responsable de l'affichage du modèle.
Le modèle englobe généralement les données (ex. UserId, FirstName) ainsi que l'état (ex. Titles) et est généralement spécifique à la vue.
MVVM
Le modèle ne contient généralement que des données (ex. UserId, FirstName) et est généralement transmis
Le modèle de vue englobe le comportement de la vue (méthodes), de ses données (modèle) et des interactions (commandes) - similaire au modèle MVP actif où le présentateur connaît le modèle. Le modèle de vue est spécifique à la vue (1 vue = 1 modèle de vue).
La vue est responsable de l'affichage des données et de la liaison de données au modèle de vue. Lorsqu'une vue est créée, son modèle de vue associé est généralement créé avec elle.
Ce que vous devez retenir, c'est que le modèle de présentation MVVM est spécifique à WPF / Silverlight en raison de leur nature de liaison de données.
La vue sait généralement à quel modèle de vue elle est associée (ou une abstraction de celle-ci).
Je vous conseille de traiter le modèle de vue comme un singleton, même s'il est instancié par vue. En d'autres termes, vous devriez pouvoir le créer via DI via un conteneur IOC et appeler des méthodes appropriées pour dire; charger son modèle en fonction des paramètres. Quelque chose comme ça:
Par exemple, dans ce cas, vous ne créeriez pas de modèle de vue spécifique à l'utilisateur mis à jour - à la place, le modèle contiendrait des données spécifiques à l'utilisateur qui seraient chargées via un appel sur le modèle de vue.
la source
Réponse courte à vos questions:
La version longue:
Nous sommes confrontés au même problème et avons trouvé certaines choses qui pourraient vous aider. Bien que je ne connaisse pas la solution "magique", ces choses soulagent un peu la douleur.
Implémentez des modèles pouvant être liés à partir de DTO pour le suivi et la validation des modifications. Ces "Data" -ViewModels ne doivent pas dépendre des services et ne proviennent pas du conteneur. Ils peuvent être simplement "nouveaux" édités, transmis et peuvent même dériver du DTO. L'essentiel est d'implémenter un modèle spécifique à votre application (comme MVC).
Découplez vos ViewModels. Caliburn facilite le couplage des ViewModels. Il le suggère même à travers son modèle Screen / Conductor. Mais ce couplage rend les ViewModels difficiles à tester, crée beaucoup de dépendances et le plus important: impose la charge de la gestion du cycle de vie de ViewModel à vos ViewModels. Une façon de les découpler est d'utiliser quelque chose comme un service de navigation ou un contrôleur ViewModel. Par exemple
interface publique IShowViewModels {void Show (object inlineArgumentsAsAnonymousType, string regionId); }
Mieux encore, cela se fait par une forme de messagerie. Mais l'important n'est pas de gérer le cycle de vie de ViewModel à partir d'autres ViewModels. Dans MVC, les contrôleurs ne dépendent pas les uns des autres, et dans MVVM ViewModels ne doivent pas dépendre les uns des autres. Intégrez-les par d'autres moyens.
INeedData<T1,T2,...>
et d'appliquer des paramètres de création de type sécurisé, cela n'en vaut pas la peine. La création d'usines pour chaque type de ViewModel n'en vaut pas la peine. La plupart des conteneurs IoC fournissent des solutions à cela. Vous obtiendrez des erreurs lors de l'exécution, mais le découplage et la testabilité de l'unité en valent la peine. Vous faites toujours une sorte de test d'intégration et ces erreurs sont facilement repérées.la source
La façon dont je le fais habituellement (en utilisant PRISM), c'est que chaque assemblage contient un module d'initialisation de conteneur, où toutes les interfaces, les instances sont enregistrées au démarrage.
Et étant donné vos exemples de classes, serait implémenté comme ceci, avec le conteneur passé tout le long. De cette façon, toutes les nouvelles dépendances peuvent être ajoutées facilement car vous avez déjà accès au conteneur.
Il est assez courant d'avoir une classe ViewModelBase, dont tous vos modèles de vue sont dérivés, qui contient une référence au conteneur. Tant que vous prenez l'habitude de résoudre tous les modèles de vue au lieu d'
new()'ing
eux, cela devrait rendre la résolution des dépendances beaucoup plus simple.la source
Parfois, il est bon d'aller à la définition la plus simple plutôt qu'à un exemple complet: http://en.wikipedia.org/wiki/Model_View_ViewModel peut-être que la lecture de l'exemple Java ZK est plus éclairante que celle de C #.
D'autres fois, écoutez votre instinct ...
Vos modèles sont-ils des mappages objet par table? Peut-être qu'un ORM aiderait à mapper aux objets de domaine tout en gérant l'entreprise ou en mettant à jour plusieurs tables.
la source