Dans son livre "Clean Architecture", l'oncle Bob dit que le présentateur devrait mettre les données qu'il reçoit dans quelque chose qu'il appelle le "View Model".
Est-ce la même chose que le "ViewModel" du modèle de conception Model-View-ViewModel (MVVM) ou est-ce un simple objet de transfert de données (DTO)?
Si c'est pas un simple DTO, comment est-il lié à la vue? La vue en obtient-elle des mises à jour via une relation Observateur?
Je suppose que cela ressemble plus au ViewModel de MVVM, car au chapitre 23 de son livre, Robert Martin dit:
Le travail [du présentateur] consiste à accepter les données de l'application et à les formater pour la présentation afin que la vue puisse simplement les déplacer à l'écran. Par exemple, si l'application souhaite afficher une date dans un champ, elle remettra au présentateur un objet Date. Le présentateur formatera ensuite ces données dans une chaîne appropriée et les placera dans une structure de données simple appelée le modèle de vue, où la vue peut les trouver.
Cela implique que la vue est en quelque sorte connectée au ViewModel, au lieu de simplement la recevoir comme argument de fonction par exemple (comme ce serait le cas avec un DTO).
Une autre raison pour laquelle je pense que c'est parce que si vous regardez l'image, le présentateur utilise le modèle de vue, mais pas la vue. Alors que le présentateur utilise les deux la limite de sortie et le DTO des données de sortie.
S'il ne s'agit ni d'un DTO ni du ViewModel de MVVM, veuillez préciser de quoi il s'agit.
Est-ce la même chose que le "ViewModel" du modèle de conception Model-View-ViewModel (MVVM)
Nan.
Ce serait ceci :
Cela a des cycles. Oncle Bob a été évitant soigneusement les cycles .
Au lieu de cela, vous avez ceci:
Qui n'a certainement pas de cycles. Mais vous vous demandez comment la vue connaît une mise à jour. Nous y reviendrons dans un instant.
ou s'agit-il d'un simple objet de transfert de données (DTO)?
Pour citer Bob de la page précédente:
Vous pouvez utiliser des structures de base ou des objets de transfert de données simples si vous le souhaitez. Vous pouvez également l'intégrer dans une table de hachage ou la construire dans un objet.
Architecture propre p207
Alors, bien sûr, si vous le souhaitez.
Mais je soupçonne fortement que ce qui vous embête vraiment est this :
Ce petit abus mignon d'UML contraste la direction de la dépendance du code source avec la direction du flux de contrôle. C'est là que se trouve la réponse à votre question.
Dans une relation d'utilisation:
le flux de contrôle va dans le même sens que la dépendance du code source.
Dans une relation de mise en œuvre:
le flux de contrôle va généralement dans la direction opposée à la dépendance du code source.
Ce qui signifie que vous regardez vraiment ceci:
Vous devriez être en mesure de voir que le flux de contrôle ne va jamais passer du présentateur à la vue.
Comment est-ce possible? Qu'est-ce que ça veut dire?
Cela signifie que la vue a son propre thread (ce qui n'est pas si inhabituel) ou (comme le souligne @Euphoric) que le flux de contrôle entre dans la vue à partir d'autre chose non représenté ici.
S'il s'agit du même thread, la vue saura quand le modèle de vue sera prêt à être lu. Mais si c'est le cas et que la vue est une interface graphique, il sera difficile de repeindre l'écran lorsque l'utilisateur le déplace pendant qu'il attend la base de données.
Si la vue a son propre thread, elle a son propre flux de contrôle. Cela signifie que pour l'implémenter, la vue devra interroger le modèle de vue pour remarquer les changements.
Puisque le présentateur ne sait pas que la vue existe et que la vue ne sait pas que le présentateur existe, ils ne peuvent pas s’appeler du tout. Ils ne peuvent pas se lancer des événements. Tout ce qui peut arriver, c'est que le présentateur écrira sur le modèle de vue et que la vue lira le modèle de vue. Chaque fois que ça en a envie.
Selon ce diagramme, la seule chose que le partage View et Presenter est la connaissance du View-Model. Et ce n'est qu'une structure de données. Ne vous attendez donc pas à ce qu'il ait un comportement.
Cela peut sembler impossible mais il peut être fait fonctionner même si le View-Model est complexe. Un petit champ mis à jour est tout ce que la vue devrait interroger pour détecter un changement.
Maintenant, bien sûr, vous pouvez insister sur l'utilisation du modèle d'observateur, ou demander à quelque chose de structuré de vous cacher ce problème, mais veuillez comprendre que vous n'avez pas à le faire.
Voici un peu de plaisir que j'ai eu pour illustrer le flux de contrôle:
Notez que chaque fois que vous voyez le flux aller à l'encontre des directions que j'ai définies précédemment, ce que vous voyez est un retour d'appel. Cette astuce ne nous aidera pas à accéder à la vue. Eh bien, sauf si nous revenons d'abord à ce qu'on appelle le contrôleur. Ou vous pouvez simplement changer la conception afin que vous puissiez accéder à la vue. Cela corrige également ce qui ressemble au début d'un problème yo-yo avec Data Access et son interface.
La seule autre chose à apprendre ici, en plus de cela, est que l'interpréteur de cas d'utilisation peut à peu près appeler les choses dans l'ordre qu'il souhaite tant qu'il appelle le présentateur en dernier.
Je trouve ce problème trop déroutant et cela prendrait beaucoup de texte et de temps pour expliquer correctement le problème car je pense que vous comprenez mal Martin's Clean Architecture et MVVM.
La première chose à noter est que le diagramme que vous avez publié est incomplet. Il ne montre que la "logique métier", mais il manque une sorte d '"orchestrateur" qui fait réellement bouger les pièces dans le bon ordre.
Le code d'orchestrateur serait aussi simple que
string Request(string request) // returns response
{
Controller.Run(data);
Presenter.Run();
return View.Run();
}
Je crois avoir entendu Martin en parler dans l'un de ses discours sur l'architecture propre.
Une autre chose à souligner est que la remarque de candied_orange sur le manque de cycles est fausse. Oui, les cyclés n'existent pas (et ne devraient pas) dans l'architecture du code. Mais les cycles entre les instances d'exécution sont courants et conduisent souvent à une conception plus simple.
C'est le cas dans MVVM. Dans MVVM, View dépend de ViewModel, et ViewModel utilise des événements pour informer View de ses modifications. Cela signifie que dans la conception des classes, il n'y a que des dépendances entre les classes View et Model, mais pendant l'exécution, il existe une dépendance cyclique entre les instances de View et de ViewModel. Pour cette raison, il n'y a pas besoin d'orchestrateur, car ViewModel fournira un moyen d'affichage pour savoir quand se mettre à jour. C'est pourquoi les "notifications" dans ce diagramme utilisent une ligne "squigly" et non une ligne directe. Cela signifie que View observe les changements dans ViewModel, et non que ViewModel dépend de View.
La chose la plus importante que vous devriez retenir de Martin's Clean Architecture n'est pas la conception elle-même, mais la façon dont vous gérez les dépendances. L'un des points critiques qu'il soulève dans ses exposés est que lorsqu'il y a une frontière, toutes les dépendances de code traversant cette frontière la traversent dans une seule direction. Dans le diagramme, cette frontière est représentée par une double ligne. Et il y a beaucoup d'inversion de dépendance via les interfaces (InputBoundary
, OutputBoundary
et DataAccessInterface
) qui corrige la direction de la dépendance du code.
En revanche, le ViewModel
dans Clean Architecture est tout simplement DTO sans logique. Ceci est rendu évident par <DS>
tag. Et c'est la raison pour laquelle orchestrator
est nécessaire, car View
ne saura pas quand exécuter sa logique.
Si je devais "aplatir" le diagramme en quoi ressemblerait-il pendant l'exécution, il ressemblerait à ceci:
Ainsi, lors de l'exécution, les dépendances sont dans une "mauvaise" direction, mais c'est très bien.
Je recommande de regarder son discours sur l'architecture propre pour mieux comprendre son raisonnement.