Injection de dépendance: Dois-je créer une classe Car pour contenir toutes ses parties?

10

J'ai de nombreuses voitures dans mon application C ++ toutes contenues avec un RaceTrack.

Chaque voiture se compose de centaines de pièces. Chaque partie dépend d'une ou deux autres parties.

J'ai lu beaucoup de questions SO sur DI et le livre de Mark Seemann et il semble que je ne devrais pas définir une classe de voiture juste pour contenir des pièces de voiture, car toutes les pièces de voiture dépendront les unes des autres et cette soupe de pièces est une voiture. Ai-je raison?

Donc, lorsque je place toutes mes voitures de course dans le RaceTrack, il n'y aura pas d'entités automobiles, mais beaucoup de pièces de voiture dépendant les unes des autres sur la piste ?? Ai-je raison?

ÉDITER:

Pendant des siècles, j'ai programmé des cours de voiture, car il était évident pour moi que j'avais besoin d'un cours de voiture si je programmais une logique de voiture. Mais avec DI, ce n'est pas si évident pour moi. Je me demande toujours si c'est idiomatique pour DI de ne pas créer une classe Car si je n'ai pas de rôle défini pour elle.

Je veux dire, est-ce correct d'avoir un volant pour la conduite, des boulons et des écrous sur roues pour l'équipage des stands et toutes sortes d'autres interfaces amusantes sans avoir d'instance qui représente une voiture dans son ensemble?

Anton Petrov
la source

Réponses:

24

À quoi bon un tas de pièces de voiture assis sur une piste de course qui fait quelqu'un?

Si votre classe de voiture ne contient que des pièces détachées, elle est aussi utile qu'un sac de pièces humides.

Usage

En tant que conducteur, ce que je veux, c'est quelque chose que je peux contrôler. Cela répond quand je demande de la vitesse. Ça se manie comme sur des rails. Cela peut s'arrêter un sou.

Ce que je veux, c'est une classe de voiture utilisable. Que je peux dire pour faire les choses sans avoir à penser au fonctionnement du carburateur. Je pense juste à la pédale d'accélérateur. Je ne m'inquiète pas de savoir comment ces éléments sont connectés. Voilà l'abstraction.

Injection de dépendance

L'injection de dépendance n'a rien à voir avec cela. Quand je conduis la voiture, je ne pense pas à la façon dont elle a été construite. Tant que cela fonctionne, je me fiche de la façon dont ils l'ont mis en place.

Non, DI est ce qui permet à mon équipage de remplacer rapidement mes pneus pour de meilleurs pneus quand il commence à pleuvoir sur la piste. C'est agréable de pouvoir le faire sans avoir à monter dans une voiture complètement différente.

DI consiste vraiment à suivre un principe: utilisation séparée de la construction.

Une voiture qui peut installer de nouveaux pneus à 90 miles à l'heure peut sembler cool, mais je ne pense pas que cela va gagner des courses avec un engin d'installation de pneus.

DI consiste à installer vos pièces de manière à ce que votre équipe de stands puisse y accéder. Lorsque vous utilisez newau même endroit que vous programmez le comportement, c'est comme si vous soudiez le carburateur en place. Bien sûr, une torche à acétylène pourrait l'enlever, mais veuillez d'abord utiliser des écrous et des boulons.

Voilà ce qu'est DI. Bien sûr, ce n'est pas aussi simple que d' newingérer quelque chose dès que vous réalisez que vous le voulez. Au lieu de cela, vous devez écrire un code distinct qui sait comment construire votre voiture. Mais il est sûr qu'il est plus facile de changer les choses plus tard. Et cela signifie que vous n'avez pas à faire glisser une usine de montage de voitures autour de la piste avec vous.

Construction

Quelque chose, quelque part, doit savoir si les pneus sont Goodyear. Où alors mettre le code de construction automobile? Si ce n'est pas la voiture, l'équipage des stands? Non. La piste? Non. Tous ceux-ci ont un code de comportement. Code qui doit être performant pendant la course. La construction de la voiture doit avoir lieu avant la course dans un endroit retiré du code de comportement. Mark Seemans a appelé cet endroit la racine de composition . La plupart des gens l'appellent principale.

C'est un schéma simple. En général, construisez le graphe objet puis appelez une méthode comportementale sur un objet dans le graphe objet. C'est ça. C'est le SEUL lieu où la construction et le comportement doivent être ensemble.

Cela ne signifie pas que la construction doit être une pile de code procédural, tous disposés de manière séquentielle en principal. Vous êtes libre d'utiliser tous les outils de la langue pour faire de la construction. Ne mélangez pas cela avec le comportement.

Faire cela dans le langage et ne pas utiliser de framework DI ou de conteneur IoC est appelé DI pur . Il fonctionne très bien. A depuis longtemps. Nous l'appelions simplement passage de référence .

Outils DI

Ce qu'un outil DI vous achète, ce sont les détails de construction déplacés dans un langage différent (xml, json, peu importe) qui impose la séparation entre construction et comportement. Si vous ne faites pas confiance à vos collègues programmeurs pour ne pas l'utiliser newquand ils ne devraient pas, cela peut être attrayant.

L'inconvénient est qu'il est tentant de laisser les détails de l'outil DI se propager à travers la base de code. Parfois, infecter la base de code avec des annotations propriétaires. Les exemples qu'ils fournissent encouragent certainement cela. L'outil a tendance à se déplacer dans l'espace du langage jusqu'à ce que vous ne puissiez pas simplement publier le travail en tant que travail de programmation Java mais en tant que travail de programmation Java / Spring.

Principes de conception

Pendant des siècles, j'ai programmé des cours de voiture, car il était évident pour moi que j'avais besoin d'un cours de voiture si je programmais une logique de voiture. Mais avec DI, ce n'est pas si évident pour moi. Je me demande toujours si c'est idiomatique pour DI de ne pas créer une classe Car si je n'ai pas de rôle défini pour elle.

Je pense que vous apprenez l'abstraction et changez la façon dont vous décidez qu'un cours est nécessaire. C'est bon. Mais ce n'est pas à propos de DI. DI ne vous aide pas à décider si vous avez besoin d'une classe de voiture. DI vous aide à empêcher la voiture de savoir, et donc à prendre soin, si les pneus sont des pneus Goodyear. DI vous aide à garder la trace de savoir si les voitures sont fabriquées au Japon.

L'une des questions les plus fondamentales de la conception de logiciels est "qu'est-ce qui sait quoi?" C'est la principale chose qu'un diagramme UML vous montre. Lorsque vous créez quelque chose que vous atteignez, c'est l'interface avec la chose concrète à laquelle vous êtes maintenant lié. La voiture doit maintenant savoir que les pneus sont Goodyear. Ce qui est un peu nul si Michelin veut vous parrainer.

Éviter de faire cela est appelé suivant le principe d'inversion de dépendance . Formellement, un module de haut niveau (comme la classe de voiture) ne devrait pas dépendre directement de modules de bas niveau (comme la classe GoodyearTire). Cela devrait dépendre d'une abstraction (comme une interface Tire).

Un moyen d'éviter cela est appelé Inversion de contrôle . Ici, l'accent est mis sur la modification du flux de contrôle. Les pneus déplacent-ils la voiture ou la voiture déplace-t-elle les pneus? Penser à cela de la bonne façon nous permet de ne pas coupler statiquement la voiture et les pneus ensemble. DI est une façon particulière de suivre l'inversion de contrôle.

Rien de tout cela ne vous indique si vous avez besoin d'un cours de voiture. Si vous programmez la "logique de voiture", c'est bien s'il y a un endroit pour le garder plutôt que de le disperser partout. Ne vous laissez pas berner en pensant que la logique de construction d'une voiture est la même que la logique du comportement d'une voiture, donc tout doit vivre au même endroit. Mais si vous n'avez pas de roulis défini pour une voiture, vous n'avez pas besoin non plus. Faites la course de motos sur la piste si vous le souhaitez.

Je veux dire, est-ce correct d'avoir un volant pour la conduite, des boulons et des écrous sur roues pour l'équipage des stands et toutes sortes d'autres interfaces amusantes sans avoir d'instance qui représente une voiture dans son ensemble?

DI ou non DI, c'est bien d'avoir une instance qui représente une voiture dans son ensemble, mais cette instance n'est pas quelque chose que je veux savoir directement si je n'ai pas à le faire. Donnez-moi un abstraction de voiture afin que je n'aie pas à me soucier si elle fonctionne au gaz, au diesel ou à l'électricité lorsque je l'utilise. C'est seulement quelque chose dont je devrais me soucier lorsque je le construis ou le maintiens. C'est bien si le code qui utilise la voiture n'a pas besoin de savoir ou de se soucier de son fonctionnement. "Je ne sais pas. Je ne veux pas savoir."

candied_orange
la source
Ce serait cool si vous n'utilisiez pas la métaphore de la voiture et de l'équipage des stands pour une question qui les utilise pour représenter le code. Mais quand même, une bonne réponse.
S. Tarık Çetin
6
Et j'ai toujours pensé que l'inversion de contrôle était quand vous restez à gauche mais la voiture tourne à droite ...
mkrieger1
CandiedOrange, donc pas de classe Car si elle n'a pas d'interface significative et de responsabilité bien définie? Et pas de Car.h dans mon projet. Et un collègue qui examine mon code et se demande s'il s'agit d'une boutique en ligne de pièces détachées automobiles ou d'une application de gestion de garage? :)
Anton Petrov
@AntonPetrov "si cela ressemble à un canard et quacks comme un canard, mais a besoin de piles, vous avez probablement la mauvaise abstraction". Les bons noms sont très importants et peuvent être difficiles à penser, mais devraient être modifiés pour refléter la responsabilité bien définie et l'interface significative afin qu'ils ne soient pas une surprise. Si je lis le nom, regarde l'interface et pense "c'est à peu près ce à quoi je m'attendais" alors tu as un bon nom.
candied_orange
15

il semble que je ne devrais pas définir une classe de voiture juste pour contenir des pièces de voiture, car toutes les pièces de voiture dépendront les unes des autres et cette soupe de pièces est une voiture. Ai-je raison?

Non.

Le point d'injection de dépendance n'est pas du tout de décourager les dépendances. Plutôt l'inverse.

L' injection de dépendance est à la question: d' où la voiture se ses pièces à partir ? Où et comment sont-ils créés et comment la voiture obtient-elle des références?

Naïvement, la voiture créerait elle-même ses pièces en appelant new. C'est facile et simple, mais c'est très rigide. La voiture doit savoir tout ce qui est nécessaire pour créer les pièces, et si jamais vous voulez avoir deux voitures avec des pièces différentes, cette logique doit également entrer dans la voiture.

Avec l'injection de dépendances, toute cette logique est retirée de la voiture et placée dans un composant de configuration, qui crée toutes les pièces et connecte les références. Les dépendances entièrement configurées sont injectées dans la voiture - et entre elles.

Michael Borgwardt
la source
1
Enfin, une explication sensée du dependency injectionconcept.
anatoly techtonik
1
Je dirais que le système le plus courant pour l'approche "naïve" n'est pas que la voiture appellera nouvelle, mais que l'utilisateur de la classe de voiture attribue ses pièces à ses propriétés, et en attendant la classe de voiture serait un état indéterminé. DI fournit un moyen d'assurer mécaniquement la validité de la classe.
whatsisname
2
@whatsisname Je peux facilement utiliser DI pour créer des objets invalides et à moitié initialisés. La validation n'est pas une responsabilité DI. Elle n'est pas non plus immuable. DI peut assumer la tâche de construire des objets valides et immuables. DI n'a aucun moyen d'imposer que ce sont les seules façons dont ces objets sont construits et utilisés. Les objets doivent appliquer cela.
candied_orange
@CandiedOrange: pour que vous puissiez facilement utiliser n'importe quelle technique pour faire quelque chose à moitié, alors quoi? Exiger des dépendances chez le constructeur, et les rendre immuables, n'est pas une solution miracle, mais c'est néanmoins un moyen très utile de prévenir de nombreuses mauvaises situations courantes.
whatsisname
0

Donc, lorsque je place toutes mes voitures de course dans le RaceTrack, il n'y aura pas d'entités automobiles, mais beaucoup de pièces de voiture dépendant les unes des autres sur la piste ?? Ai-je raison?

Bien. Oui et non. Une entité automobile est toujours valable. Et une voiture est plus que la somme de ses parties. Vous avez également besoin d'un chauffeur (pour le moment). Plusieurs fois, les voitures de course ont également des noms, sans parler de la marque et du modèle de la voiture.

Oui, les voitures sont fondamentalement un conteneur de pièces, mais c'est cet emballage et cette coopération de pièces qui composent quelque chose de plus grand: une voiture.

Alors vraiment, non. Une voiture n'est pas seulement un sac aléatoire de métal et de plastique. C'est une machine.

L'injection de dépendance n'est pas exagérée dans ce cas. Quelque chose d'autre doit construire la voiture, qui serait une classe d'usine ou un objet Builder. La voiture ne doit pas savoir se construire.

Greg Burghardt
la source
2
Vous pouvez avoir DI sans usines ni objets constructeur. Les paramètres de constructeur simples sont une méthode valide qui fonctionne dans de nombreuses circonstances.
whatsisname