Il y a un vieux, vieux débat en cours sur la meilleure façon de faire l'injection de dépendance.
La coupe originale de Spring a instancié un objet simple, puis injecté des dépendances via des méthodes de définition.
Mais alors, une grande contingence de personnes a insisté sur le fait que l'injection de dépendances via les paramètres du constructeur était la bonne façon de le faire.
Puis, ces derniers temps, alors que l'utilisation de la réflexion est devenue plus courante, la fixation directe des valeurs des membres privés, sans arguments ni constructeurs, est devenue à la mode.
Votre premier constructeur est donc cohérent avec la deuxième approche de l'injection de dépendances. Il vous permet de faire de belles choses comme injecter des simulations pour les tests.
Mais le constructeur sans argument a ce problème. Puisqu'il instancie les classes d' implémentation pour PaypalCreditCardProcessor
et DatabaseTransactionLog
, il crée une dépendance difficile et à la compilation avec PayPal et la base de données. Il prend la responsabilité de construire et de configurer correctement tout cet arbre de dépendances.
Imaginez que le processeur PayPay est un sous-système vraiment compliqué et tire en outre de nombreuses bibliothèques de support. En créant une dépendance au moment de la compilation sur cette classe d'implémentation, vous créez un lien incassable vers cet arbre de dépendance entier. La complexité de votre graphique d'objet vient de bondir d'un ordre de grandeur, peut-être deux.
Un grand nombre de ces éléments dans l'arborescence des dépendances seront transparents, mais beaucoup d'entre eux devront également être instanciés. Il y a de fortes chances que vous ne puissiez pas simplement instancier a PaypalCreditCardProcessor
.
En plus de l'instanciation, chacun des objets aura besoin de propriétés appliquées à partir de la configuration.
Si vous n'avez qu'une dépendance sur l'interface, et autorisez une fabrique externe à construire et à injecter la dépendance, ils vous coupent tout l'arborescence des dépendances PayPal, et la complexité de votre code s'arrête à l'interface.
Il y a d'autres avantages, comme la spécification des classes d'implémentation dans la configuration (c'est-à-dire au moment de l'exécution plutôt que lors de la compilation), ou la spécification de dépendances plus dynamique qui varie, par exemple, selon l'environnement (test, intégration, production).
Par exemple, disons que PayPalProcessor avait 3 objets dépendants, et chacune de ces dépendances en avait deux autres. Et tous ces objets doivent extraire les propriétés de la configuration. Le code en l'état assumerait la responsabilité de construire tout cela, de définir les propriétés à partir de la configuration, etc. etc. - toutes les préoccupations dont le cadre DI prendra soin.
Il peut ne pas sembler évident au premier abord de quoi vous vous protégez en utilisant un cadre DI, mais il s'additionne et devient douloureusement évident au fil du temps. (lol je parle de l'expérience d'avoir essayé de le faire à la dure)
...
Dans la pratique, même pour un programme vraiment minuscule, je trouve que je finis par écrire dans un style DI, et divise les classes en paires implémentation / usine. Autrement dit, si je n'utilise pas un framework DI comme Spring, je jette juste ensemble quelques classes d'usine simples.
Cela permet de séparer les préoccupations afin que ma classe puisse tout faire, et la classe d'usine assume la responsabilité de construire et de configurer les choses.
Pas une approche requise, mais FWIW
...
Plus généralement, le modèle DI / interface réduit la complexité de votre code en faisant deux choses:
En plus de cela, puisque l'instanciation et la configuration d'objets sont une tâche assez familière, le framework DI peut réaliser de nombreuses économies d'échelle grâce à une notation standardisée et à des astuces comme la réflexion. La diffusion de ces mêmes préoccupations autour des cours finit par ajouter beaucoup plus d'encombrement qu'on ne le pense.