Comment éviter une quantité folle d'interfaces dans l'interface utilisateur avec l'injection de dépendance?

8

Problème
J'ai récemment lu beaucoup de choses sur les singletons étant mauvais et comment l'injection de dépendances (que je comprends comme "utilisant des interfaces") est meilleure. Lorsque j'ai implémenté une partie de cela avec des rappels / interfaces / DI et en adhérant au principe de ségrégation d'interface, je me suis retrouvé avec un sacré bordel.

Les dépendances d'un parent d'interface utilisateur où, fondamentalement, celles de tous ses enfants se sont combinées, de sorte que plus la hiérarchie d'un élément d'interface utilisateur était élevée, plus son constructeur était gonflé.

Tout en haut de la hiérarchie de l'interface utilisateur se trouvait une classe Application, contenant les informations sur la sélection actuelle et une référence à un modèle 3D qui doit refléter les changements. La classe d'application implémentait 8 interfaces, et ce n'était qu'un rond-point sur un cinquième des produits (/ interfaces) à venir!

Je travaille actuellement avec un singleton contenant la sélection actuelle et les éléments d'interface utilisateur ayant une fonction pour se mettre à jour. Cette fonction fait couler l'arborescence de l'interface utilisateur et les éléments de l'interface utilisateur, puis accède au singleton de sélection actuel selon les besoins. Le code me semble plus propre de cette façon.

Question
Un singleton peut-il convenir à ce projet?
Sinon, y a-t-il un défaut fondamental dans ma façon de penser et / ou de mettre en œuvre l'ID qui le rend si lourd?

Informations supplémentaires sur le projet
Type: Panier pour les appartements, avec cloches et sifflets
Taille: 2 mois-homme pour le code et l'interface utilisateur
Maintenance: Aucune mise à jour en cours d'exécution, mais peut-être "version 2.0" plus tard
Environnement: Utilisation de C # dans Unity, qui utilise une entité Système de composants

Dans presque tous les cas, l'interaction utilisateur déclenche plusieurs actions. Par exemple, lorsque l'utilisateur sélectionne un élément

  • la partie de l'interface utilisateur montrant cet élément et sa description doit être mise à jour. Pour cela, il doit également obtenir des informations à partir d'un modèle 3D afin de calculer le prix.
  • plus haut dans l'interface utilisateur, le prix total global doit être mis à jour
  • une fonction correspondante dans une classe sur un modèle 3D doit être appelée pour y afficher les changements
R. Schmitz
la source
DI ne consiste pas seulement à utiliser des interfaces, c'est la possibilité d'échanger des concrétions qui est particulièrement utile dans le domaine des tests unitaires ...
Robbie Dee
1
De plus, je n'ai pas encore vu de problème où un singleton est la solution préférée. L'exemple canonique que les gens semblent toujours indiquer est un rédacteur de journaux, mais même ici, vous voudrez peut-être écrire dans un certain nombre de journaux différents (erreur, débogage, etc.).
Robbie Dee
@RobbieDee Oui, DI est à peu près plus, mais je ne peux pas voir une situation où l'utilisation d'une interface n'est pas DI. Et si un singleton n'est pas la solution préférée - ce que je suppose également - alors pourquoi la solution préférée est-elle si désordonnée? C'est ma principale question, mais je voulais la garder ouverte, car parfois la règle générale ne s'applique pas.
R. Schmitz
L'arrangement interface-concrétion est également une caractéristique des cadres de simulation. Ils ont également été dans les langues OO pendant quelques bonnes décennies ...
Robbie Dee
Je ne comprends pas votre point.
R. Schmitz

Réponses:

4

Je pense que la question est un symptôme, pas une solution.

J'ai récemment lu beaucoup de choses sur les singletons étant mauvais et comment l'injection de dépendances (que je comprends comme "utilisant des interfaces") est meilleure. Lorsque j'ai implémenté une partie de cela avec des rappels / interfaces / DI et en adhérant au principe de ségrégation d'interface, je me suis retrouvé avec un sacré bordel.

Une solution à la recherche d'un problème; cela et l'incompréhension est probablement en train de corrompre votre conception. Avez-vous lu cette question SO sur DI vs Singleton, différents concepts ou non? J'ai lu cela comme enveloppant simplement un singleton pour que le client n'ait pas à faire face à un singleton. C'est. Juste. Une. Bonne. Encapsulation. Je pense que c'est parfait.


plus un élément de l'interface utilisateur était haut dans la hiérarchie, plus son constructeur était gonflé.

Construisez d'abord les petits morceaux, puis passez-les dans le constructeur de la chose à laquelle ils appartiennent et passez cette plus grande chose dans le constructeur de la prochaine chose la plus grande ...

Si vous avez des problèmes de construction complexes, utilisez le modèle d'usine ou de constructeur. En bout de ligne, il y a cette construction complexe tirée dans sa propre classe pour garder les autres classes propres, propres, compréhensibles, etc.


La classe d'application implémentait 8 interfaces, et ce n'était qu'un rond-point sur un cinquième des produits (/ interfaces) à venir!

Cela ressemble à l'absence de capacité d'extension. La conception de base est manquante et tout est entassé par le haut. Il devrait y avoir plus de construction, de composition et d'héritage ascendant.

Je me demande si votre design est "top heavy". On dirait que nous essayons de faire d'une classe tout ou n'importe quoi. Et le fait qu'il s'agisse d'une classe d'interface utilisateur, et non d'une classe de domaine métier, me fait vraiment me poser des questions sur la séparation des préoccupations.

Revoyez votre conception depuis le début et assurez-vous qu'il existe une abstraction de produit solide et de base sur laquelle vous pouvez vous baser pour créer des productions de catégories plus complexes ou différentes. Ensuite, vous feriez mieux d'avoir des collections personnalisées de ces choses, donc vous avez un endroit où mettre la fonctionnalité "au niveau de la collection" - comme ce "3ème modèle" que vous mentionnez.


... modèle 3D qui doit refléter les changements.

Une grande partie de cela peut tenir dans des classes de collection personnalisées. Il peut également s'agir d'une structure de classe indépendante en soi en raison de la profondeur et de la complexité. Ces deux choses ne s'excluent pas mutuellement.

Lisez à propos du modèle de visiteur. C'est l'idée d'avoir des mandrins entiers de fonctionnalités câblés de manière abstraite à différents types.


Design et DI

90% de toute l'injection de dépendance que vous ferez jamais est le passage des paramètres du constructeur. C'est ce que dit le quy qui a écrit le livre . Concevez bien vos classes et évitez de polluer ce processus de réflexion avec une notion vague sur la nécessité d'utiliser un bidon DI. Si vous en avez besoin, votre design vous le proposera pour ainsi dire.


Concentrez-vous sur la modélisation du domaine commercial des appartements.

Évitez l'approche Jessica Simpson de la conception : "Je ne sais absolument pas ce que cela signifie, mais je le veux."

Les éléments suivants sont tout simplement faux:

  • Je suis censé utiliser des interfaces
  • Je ne suis pas censé utiliser un singleton
  • J'ai besoin de DI (quoi que ce soit)
  • Je suis censé utiliser la composition, pas l'héritage
  • Je suis censé éviter l'héritage
  • J'ai besoin d'utiliser des modèles
radarbob
la source
J'ai essayé de me débarrasser de tous les singletons que j'utilisais et certaines des choses que vous avez dites se sont produites plus ou moins automatiquement. Le reste a aussi beaucoup de sens et je vais suivre vos conseils et lire le modèle des visiteurs, etc. Dans l'ensemble, une réponse bien écrite, merci!
R. Schmitz
Juste un point, et je n'essaie pas d'être un vampire aidant ici, mais cela faisait en quelque sorte partie de la question initiale: le consensus général (et il est basé sur des raisons valables) semble être que vous n'êtes en effet pas censé utilisez un singleton. Vous écrivez "" Je ne suis pas censé utiliser un singleton "est faux" - dans quelle situation serait-il approprié d'en utiliser un alors?
R. Schmitz
Tout ce que je dis, c'est "ça dépend". Trop souvent, je vois des maximes prises au pied de la lettre.
radarbob
3

"Hiérarchie des classes" est un peu un drapeau rouge. Hypothétique: une page Web avec 5 widgets. La page n'est l'ancêtre d'aucun des widgets. Il peut contenir une référence à ces widgets. Mais ce n'est pas un ancêtre. Au lieu de la hiérarchie des classes, pensez à utiliser la composition. Chacun des 5 widgets peut être construit seul, sans référence aux autres widgets ou à la page d'accueil. La page d'accueil est ensuite construite avec suffisamment d'informations pour créer une page de base et disposer les objets widget (Collection) qui lui sont transmis. La page est responsable de la mise en page, etc., mais pas de la construction et de la logique des widgets.

Une fois que vous utilisez la composition, DI est votre meilleur ami. DI vous permet d'échanger chaque widget pour n'importe quelle version ou type de widget que vous définissez dans DI. La construction des widgets est capturée dans l'ID et est distincte de la page supérieure. Peut-être même que la composition de la collection peut être définie dans votre DI. La première page effectue la mise en page en fonction des widgets qui lui sont transmis, comme il se doit. Aucune modification du constructeur de la page d'accueil n'est nécessaire. La première page n'a besoin que de la logique pour effectuer la mise en page des widgets et transmettre des informations vers / depuis le widget en fonction des interfaces définies.

Au lieu de faire passer des auditeurs de haut en bas de la chaîne de composition, injectez une collection d'auditeurs aux widgets qui en ont besoin. Injectez la collection dans un éditeur afin qu'il puisse publier dans la collection. Injectez la collection dans les écouteurs afin qu'ils puissent s'ajouter à la collection. La collection recoupe tous les objets de la chaîne de composition.

scorpdaddy
la source
Désolé, la "hiérarchie des classes" était une mauvaise formulation ici; ce n'était en fait pas une hiérarchie de classes, mais plutôt une hiérarchie d'interface utilisateur. Les «widgets» ne sont pas des sous-classes de la classe d'application, mais la «page» contient des références à ces derniers afin de publier les mises à jour de l'interface utilisateur. C'était très testable avec cette structure, mais il y avait aussi beaucoup de frais généraux en particulier chez les constructeurs, passant juste beaucoup d'auditeurs pour leurs enfants (UI).
R. Schmitz
@ R.Schmitz: Les frais généraux importaient-ils? L'interface utilisateur était-elle lente et le design que vous avez décrit était-il à blâmer?
Robert Harvey
@ R.Schmitz Pouvez-vous élaborer sur "transmettre de nombreux auditeurs"? Peut-être que mon expérience avec DI en C # est faible, mais quelque chose me dit que ce ne serait pas nécessaire (au moins dans le constructeur) avec une conception appropriée.
Katana314
@RobertHarvey Aucune perte de performances, il s'agit uniquement de lisibilité.
R. Schmitz
@ Katana314 Par exemple, lorsqu'un élément est ajouté, le modèle 3d doit être mis à jour et n'appartient à aucune des catégories de produits, il est donc référencé dans la classe d'application, qui écoute les éléments ajoutés / supprimés / modifiés. Au final, ce serait à peu près la même chose pour chaque catégorie de produits. D'autres parties de l'interface utilisateur réagissent également (et écoutent), mais le message doit remonter en haut, car c'est là que se trouvent la référence 3dmodel et les données réelles (qui sont ensuite également mises à jour).
R. Schmitz