Je crée une application WPF en utilisant le modèle MVVM. À l'heure actuelle, mes viewmodels appellent la couche de service pour récupérer des modèles (ce qui n'est pas pertinent pour le viewmodel) et les convertir en viewmodels. J'utilise l'injection de constructeur pour transmettre le service requis au viewmodel.
Il est facilement testable et fonctionne bien pour les modèles de vue avec peu de dépendances, mais dès que j'essaie de créer des modèles de vue pour des modèles complexes, j'ai un constructeur avec beaucoup de services injectés (un pour récupérer chaque dépendance et une liste de toutes les valeurs disponibles pour se lier à un itemsSource par exemple). Je me demande comment gérer plusieurs services comme ça et avoir toujours un modèle de vue que je peux tester facilement.
Je pense à quelques solutions:
Création d'un singleton de services (IServices) contenant tous les services disponibles en tant qu'interfaces. Exemple: Services.Current.XXXService.Retrieve (), Services.Current.YYYService.Retrieve (). De cette façon, je n'ai pas un énorme constructeur avec une tonne de paramètres de services.
Création d'une façade pour les services utilisés par le viewModel et passage de cet objet dans le ctor de mon viewmodel. Mais alors, je vais devoir créer une façade pour chacun de mes modèles de vues complexes, et ça pourrait être un peu trop ...
Selon vous, quelle est la "bonne" façon de mettre en œuvre ce type d'architecture?
la source
new
pour créer d'autres modèles de vue, mais pensez à quelque chose d'aussi simple qu'une application MDI où cliquer sur un bouton ou un menu "nouveau document" ajoutera un nouvel onglet ou ouvrira une nouvelle fenêtre. L'enveloppe / le conducteur doit être capable de créer de nouvelles instances de quelque chose , même s'il est caché derrière une ou quelques couches d'indirection.Réponses:
En fait, ces deux solutions sont mauvaises.
Il s'agit essentiellement du modèle de localisation de service , qui est un anti-modèle. Si vous faites cela, vous ne pourrez plus comprendre de quoi dépend réellement le modèle de vue sans regarder son implémentation privée, ce qui rendra très difficile le test ou la refactorisation.
Ce n'est pas tellement un anti-modèle mais c'est une odeur de code. Essentiellement, vous créez un objet paramètre , mais le motif du modèle de refactorisation PO est de traiter des ensembles de paramètres qui sont utilisés fréquemment et dans de nombreux endroits différents , alors que ce paramètre ne sera utilisé qu'une seule fois. Comme vous le mentionnez, cela créerait beaucoup de gonflement de code sans aucun avantage réel et ne serait pas agréable avec beaucoup de conteneurs IoC.
En fait, les deux stratégies ci-dessus négligent le problème global, à savoir que le couplage est trop élevé entre les modèles de vue et les services . Il suffit de cacher ces dépendances dans un localisateur de service ou d'un objet de paramètre ne fait pas changer combien d'autres objets du modèle de vue dépend.
Pensez à la façon dont vous testeriez à l'unité l'un de ces modèles de vue. Quelle sera la taille de votre code d'installation? Combien de choses doivent être initialisées pour que cela fonctionne?
Beaucoup de gens qui commencent avec MVVM essaient de créer des modèles de vue pour un écran entier , ce qui est fondamentalement la mauvaise approche. MVVM est tout au sujet de la composition , et un écran avec de nombreuses fonctions doit être composé de plusieurs modèles de vue différents, chacun dépendant d'un seul ou de quelques modèles / services internes. S'ils ont besoin de communiquer entre eux, vous le faites via pub / sub (courtier de messages, bus d'événements, etc.)
Ce que vous devez réellement faire est de refactoriser vos modèles de vue afin qu'ils aient moins de dépendances . Ensuite, si vous avez besoin d'un "écran" agrégé, vous créez un autre modèle de vue pour agréger les modèles de vue plus petits. Ce modèle de vue agrégée n'a pas grand-chose à lui seul, il est donc également assez facile à comprendre et à tester.
Si vous l'avez fait correctement, cela devrait être évident simplement en regardant le code, car vous aurez des modèles de vue courts, succincts, spécifiques et testables.
la source
Je pourrais écrire un livre à ce sujet ... en fait je le suis;)
Tout d'abord, il n'y a pas de manière universellement «correcte» de faire les choses. Vous devez prendre en compte d'autres facteurs.
Il est possible que vos services soient trop fins. Envelopper les services avec des façades qui fournissent l'interface qu'un Viewmodel spécifique ou même un cluster de ViewModels associés utiliserait pourrait être une meilleure solution.
Il serait encore plus simple de regrouper les services dans une seule façade que tous les modèles de vue utilisent. Bien sûr, cela peut potentiellement être une très grande interface avec beaucoup de fonctions inutiles pour le scénario moyen. Mais je dirais que ce n'est pas différent d'un routeur de messages qui gère tous les messages de votre système.
En fait, ce que j'ai vu évoluer beaucoup d'architectures est un bus de messages construit autour de quelque chose comme le modèle Event Aggregator. Les tests y sont faciles car votre classe de test enregistre simplement un écouteur avec l'EA et déclenche l'événement approprié en réponse. Mais c'est un scénario avancé qui prend du temps à se développer. Je dis commencer par la façade unificatrice et partir de là.
la source
Pourquoi ne pas combiner les deux?
Créez une façade et mettez tous les services que vos modèles de vue utilisent. Ensuite, vous pouvez avoir une seule façade pour tous vos modèles de vue sans le mauvais mot S.
Ou vous pouvez utiliser l'injection de propriété au lieu de l'injection de constructeur. Mais alors, vous devez vous assurer que ceux-ci sont injectés correctement.
la source