L'utilisation de DI / IoC devrait-elle supprimer toutes les occurrences du «nouveau» mot clé?

21

L'utilisation de l'injection de dépendances et d'un conteneur d'inversion de contrôle doit-elle supprimer toutes les occurrences du newmot clé " " de votre code?

En d'autres termes, chaque objet / dépendance, quelle que soit sa simplicité ou sa durée de vie, doit-il être "enregistré" dans votre conteneur IoC et injecté dans la méthode / classe qui doit les utiliser?

Si non, où tracez-vous la ligne entre les dépendances / objets enregistrés dans le conteneur IoC, par rapport à ce qui est créé "en ligne" comme référence concrète, via le newmot-clé?

CraigTP
la source
J'ai eu la même pensée, dans le sens de "est-ce que" nouveau "est un mot de 4 lettres?"
Michael Easter

Réponses:

27

Évitez le dogme. Faites ce qui vous semble bien.

Je préfère utiliser "nouveau" pour les structures de données qui n'ont aucun comportement.

Si la classe a un comportement, je regarde alors la portée de ce comportement. S'il est apatride et n'a pas de dépendances, je penche vers "nouveau". Je ne commence à refactoriser vers DI que lorsque j'ai besoin d'ajouter une dépendance aux ressources avec état (comme une base de données ou un fichier), ou à d'autres classes avec de telles ressources.

Alex Beynenson
la source
15
+1 pour "éviter le dogme". Trop facile de tomber dans le piège de simplement suivre les mouvements sans comprendre ce qui se passe ou où il doit être utilisé correctement.
Wayne Molina
@Alex j'aime ça. Une approche très pragmatique. Curieusement, j'allais ajouter un peu de code dans ma question montrant une newdépendance 'ed dans mon propre code qui a incité ma question. C'était une classe simple sans fonctionnalité ni "comportement" en soi, seulement des propriétés simples.
CraigTP
11

Dans mon livre , je fais la distinction entre les dépendances stables et volatiles . Il est très important d'utiliser DI avec des dépendances volatiles, mais souvent vous pouvez obtenir un couplage encore plus lâche en extrayant et en injectant également des dépendances stables.

Gardez à l'esprit que l'application de DI ne doit pas reposer sur un conteneur DI . Dans le cas où l' ID du pauvre est utilisé, aucun newmot-clé n'est supprimé - ils sont simplement déplacés vers la racine de composition .

Certaines personnes préfèrent en fait la DI de Pauvre aux conteneurs de DI. Le coût de maintenance peut être plus élevé, mais vous obtenez en retour une sécurité au moment de la compilation. Comme j'ai récemment entendu Dan North dire: " newest le nouveau new" :)

Ce que cela signifie est simplement qu'au lieu d'implémenter la racine de composition avec un conteneur DI, il contiendrait simplement un tas d' newinstructions imbriquées .

Mark Seemann
la source
Mark, pour développer un peu mon commentaire sur Twitter , les programmeurs sont le bon endroit pour les questions conceptuelles de tableau blanc qui ne traitent pas d'une erreur spécifique dans une implémentation.
Adam Lear
3
Stack Overflow a plus de 2000 questions dans la balise d'injection de dépendance, et beaucoup d'entre elles sont comme celle-ci. Les programmeurs ont 23 questions dans la même balise. Sur la base de ces chiffres, où un développeur devrait-il avoir le plus de chances d'obtenir une réponse à une question comme celle-ci?
Mark Seemann
1
Beaucoup de ces questions sont antérieures aux programmeurs. Ce site a été créé pour retirer les questions conceptuelles des SO et leur offrir un foyer dédié. Et nous grandissons toujours. C'est un travail en cours, mais idéalement, les questions qui n'impliquent pas de problèmes d'implémentation spécifiques seraient migrées ici. Nous faisons ce que nous pouvons en attendant.
Adam Lear
3

"nouveau" n'est pas un mot clé interdit. Mes règles personnelles:

Tous les "services" (j'appelle un service une classe conçue pour fournir une ou plusieurs méthodes liées à une et une seule chose; par exemple: accéder à la base de données, récupérer / mettre à jour les données relatives à un domaine donné) sont enregistrés dans le conteneur IOC. Aucune classe ne devrait avoir à décider comment obtenir la mise en œuvre d'un service donné dont elle a besoin. Par conséquent, chaque fois que vous devez utiliser un service existant, vous devez configurer votre classe et le conteneur IOC pour vous le fournir. Imaginez que vous ne savez pas comment fonctionne votre implémentation, il peut s'agir d'un service Web, peu vous importe.

Tous les beans ou modèles doivent être créés avec le mot-clé "new" ou avec une fabrique enregistrée par le CIO lorsque j'en ai besoin pour activer certaines propriétés par défaut. Il y a aussi le cas particulier des classes utilitaires ne contenant que des méthodes statiques (par exemple: un service utilitaire mathématique). S'ils sont totalement autonomes et n'ont besoin d'aucune connexion à une base de données ou d'un autre service enregistré auprès du CIO, je les laisse en dehors du CIO et ils doivent être appelés statiquement. Cependant, si l'une de ces classes doit utiliser un service enregistré auprès du CIO, je le change en classe singleton et l'enregistre dans le CIO.

Jalayn
la source
2

Je voudrais juste ajouter aux réponses existantes qui ont été soulevées Earler, newc'est parfaitement bien de créer des objets qui sont des objets de valeur pure (c'est-à-dire qui n'ont que des propriétés / getters / setters). Consultez le blog de test Google pour plus de détails.

Jack
la source
+1 pour le lien. Le point 9 de cet article de blog répond à la question et établit une distinction pragmatique entre ce qui devrait et ne devrait pas être newédité
CraigTP
1

Cela dépend clairement de la langue que vous utilisez. Si votre langage vous oblige à utiliser des objets pour représenter à la fois l'objet réel et les enregistrements (objet de valeur, structures), la réponse est très probablement non.

Parlant uniquement d'objets "réels", il n'y a rien de mal à créer explicitement une instance. Ce que vous devez cependant garder à l'esprit, c'est que toutes les classes doivent suivre le principe de la responsabilité unique. Il ne devrait pas être de la responsabilité d'une classe de choisir l'implémenteur d'un service sur lequel elle s'appuie. Cependant, il y a des classes qui ont la même responsabilité, c'est-à-dire de fournir des implémenteurs de certains services. Un IoC pourrait être une telle classe. En fait, tout type d’usine est une telle classe.

Donc, pour résumer: seules ces classes devraient instancier directement des objets, à qui revient la responsabilité de choisir les classes à instancier.
Vous pourriez trouver ces critères utiles: http://en.wikipedia.org/wiki/GRASP_(object-oriented_design)#Creator

back2dos
la source
1

Un mot: non.

Plus de mots: l'injection de dépendance est exactement cela. C'est un moyen par lequel vous introduisez des ressources dont un autre objet dépend, mais ne devrait pas connaître les détails complexes d'une autre source. L'utilisation de DI ne signifie pas que RIEN dans votre programme ne doit savoir comment créer un autre objet; en fait, je postule qu'il est hautement irréalisable pour un programme non trivial d'éviter complètement le "nouveau" mot clé (ou les alternatives basées sur la réflexion). Cependant, DI signifie que les objets qui ne devraient pas avoir à savoir comment créer des objets complexes qui nécessitent beaucoup de dépendances qui coupleront étroitement cet objet à l'autre, ne devraient pas avoir toutes ces connaissances étroitement couplées.

J'étudierais les deux principales théories de la conception de logiciels O / O, GRASP et SOLID. GRASP vous dira d'étudier le but de l'objet et vous demandera: "Cet objet devrait-il être responsable de la création de nouveaux objets de cet autre type? Cela fait-il partie de la" description du travail "de cet objet?" SOLID va encore plus loin: le «S» représente le «principe de responsabilité unique», qui stipule sans ambiguïté qu'un objet doit avoir un travail, et qu'il doit être le seul objet du programme qui effectue ce travail spécifique.

Donc, GRASP vous encourage généralement à regarder à travers vos classes existantes pour lesquelles créer ces nouveaux objets, et à en trouver une pour laquelle la tâche de créer cet objet correspond à ce que fait déjà l'objet, tout en maintenant votre niveau de "cohésion" souhaité. SOLID vous dira que la cohésion est la clé; la tâche de créer ces objets devrait être confiée à une fabrique qui devrait être injectée dans votre classe. Je pense que vous constaterez, à mesure que la complexité de votre programme continue de croître, que l'adhésion à l'une ou l'autre de ces méthodologies, avec une volonté de refactorisation, se traduira par des architectures très similaires.

KeithS
la source