Je sais que c'est un débat brûlant et que les opinions ont tendance à changer avec le temps en ce qui concerne la meilleure approche à adopter.
J'avais l'habitude d'utiliser exclusivement l'injection sur le terrain pour mes cours, jusqu'à ce que je commence à lire sur différents blogs (ex: petrikainulainen et schauderhaft et fowler ) sur les avantages de l'injection de constructeur. Depuis, mes méthodologies sont passées à utiliser l'injection de constructeur pour les dépendances requises et l'injection de setter pour les dépendances facultatives.
Cependant, je me suis récemment lancé dans un débat avec l'auteur de JMockit - un cadre moqueur - dans lequel il voit l'injection de constructeur et de poseur comme une mauvaise pratique et indique que la communauté JEE est d'accord avec lui.
Dans le monde actuel, y a-t-il une méthode privilégiée d'injection? L'injection sur le terrain est-elle préférable?
Ayant changé pour l'injection de constructeur à partir d'injection de champ ces dernières années, je trouve qu'il est beaucoup plus clair d'utiliser, mais je me demande si je devrais réexaminer mon point de vue. L'auteur de JMockit (Rogério Liesenfeld) est clairement au courant des techniques de DI. Je me sens donc obligé de revoir mon approche étant donné qu'il se sent si fermement opposé à l'injection de constructeur / poseur.
la source
Réponses:
L'injection de terrain est un peu trop "action fantasmagorique à distance" à mon goût.
Prenons l'exemple que vous avez fourni dans votre publication Google Groupes:
En gros, ce que vous dites, c’est que "j’ai cette classe avec un état privé, à laquelle j’ai joint des
@injectable
annotations, ce qui signifie que l’état peut être automatiquement peuplé par un agent extérieur, même si mon état a été déclaré privé. "Je comprends les motivations pour cela. C'est une tentative pour éviter une grande partie de la cérémonie qui est inhérente à la mise en place d'une classe correctement. En gros, ce que l’on dit, c’est: "Je suis fatigué d’écrire tout ce passe-partout, alors je vais simplement annoter tout mon état et laisser le conteneur de DI se charger de le régler pour moi."
C'est un point de vue parfaitement valable. Mais c’est aussi une solution de contournement pour les fonctionnalités de langage qui ne devraient sans doute pas être contournées. Aussi, pourquoi s'arrêter là? Traditionnellement, DI s’appuyait sur chaque classe ayant une interface compagnon. Pourquoi ne pas éliminer toutes ces interfaces avec des annotations?
Considérez l’alternative (cela va être C #, parce que je le connais mieux, mais il existe probablement un équivalent exact en Java):
Je sais déjà certaines choses à propos de ce cours. C'est une classe immuable. state ne peut être défini que dans le constructeur (références, dans ce cas particulier). Et comme tout provient d’une interface, je peux échanger des implémentations dans le constructeur, c’est là que vos simulacres entrent en jeu.
Maintenant, tout ce que mon conteneur DI doit faire est de réfléchir au constructeur pour déterminer quels objets il doit créer. Mais cette réflexion se fait sur un membre du public, d'une manière de première classe; c'est-à-dire que les métadonnées font déjà partie de la classe, après avoir été déclarées dans le constructeur, une méthode dont le but spécifique est de fournir à la classe les dépendances dont elle a besoin.
Certes, cela fait beaucoup de choses, mais c’est ainsi que le langage a été conçu. Les annotations semblent être un sale bidouillage pour quelque chose qui aurait dû être intégré au langage lui-même.
la source
VeracodeService
est presque identique à celle que vous avez écrite (bien que Java vs C #). LeVeracodeServiceImplTest
est en fait une classe de test unitaire. Les@Injectable
champs sont essentiellement des objets fictifs insérés dans le contexte. Le@Tested
champ est l'objet / la classe avec le constructeur DI défini. Je suis d'accord avec votre point de vue qui préfère l'injection de constructeur à l'injection de champ. Cependant, comme je l'ai mentionné, l'auteur de JMockit pense le contraire et j'essaie de comprendre pourquoiL'argument de moins de test initialisation boiletplate est valide, mais il y a d'autres préoccupations à prendre en compte. Tout d'abord, vous devez répondre à une question:
Est-ce que je veux que ma classe ne soit instanciable que par réflexion?
Utiliser des injections sur le terrain signifie réduire la compatibilité d'une classe aux environnements d'injection de dépendance qui instancient des objets en utilisant la réflexion et prennent en charge ces annotations d'injection particulières. Certaines plates-formes basées sur le langage Java ne prennent même pas en charge la réflexion ( GWT ). Par conséquent, les classes à injection de champ ne seront pas compatibles avec celles-ci.
Le deuxième problème est la performance . Un appel de constructeur (direct ou par réflexion) est toujours plus rapide qu'un groupe d'affectations de champs de réflexion. Les infrastructures d'injection de dépendance doivent utiliser l'analyse de réflexion pour créer un arbre de dépendance et créer des constructeurs de réflexion. Ce processus entraîne des pertes de performances supplémentaires.
La performance affecte la maintenabilité . Si chaque suite de tests doit être exécutée dans un conteneur d'injection de dépendance, des milliers de tests unitaires peuvent durer des dizaines de minutes. En fonction de la taille d'une base de code, cela peut être un problème.
Tout cela soulève de nombreuses nouvelles questions:
En règle générale, plus un projet est grand et important, plus ces facteurs sont importants. En outre, sur le plan de la qualité, nous aimerions généralement garder le code élevé en ce qui concerne la compatibilité, la testabilité et la maintenabilité. Du point de vue philosophique, l’injection de champ rompt l’ encapsulation , qui est l’un des quatre principes fondamentaux de la programmation orientée objet , qui est le paradigme principal de Java.
Beaucoup, beaucoup d'arguments contre l'injection de champ.
la source
L'injection sur le terrain obtient un vote "non" définitif de ma part.
Comme Robert Harvey, c'est un peu trop automagique à mon goût. Je préfère le code explicite au code implicite, et ne tolère l'indirection que comme un avantage évident, car il rend le code plus difficile à comprendre et à raisonner.
Comme Maciej Chałapuk, je n'aime pas avoir besoin de réflexion ou d'un cadre DI / IoC pour instancier une classe. C'est comme conduire à Rome pour se rendre à Paris depuis Amsterdam.
Remarquez que j'aime DI et tous les avantages que cela apporte. Juste pas sûr d'avoir besoin des frameworks pour instancier une classe. Surtout pas l'effet que les conteneurs IoC ou d'autres cadres DI peuvent avoir sur le code de test.
J'aime que mes tests soient très simples. Je n'aime pas avoir à passer par la configuration d'un conteneur IoC ou d'un autre framework DI pour le faire configurer ensuite ma classe à tester. Cela me semble bizarre.
Et cela rend mes tests dépendants de l'état global du framework. C'est-à-dire que je ne peux plus exécuter deux tests en parallèle nécessitant la configuration d'une instance de classe X avec différentes dépendances.
Au moins avec l'injection de constructeur et de setter, vous avez la possibilité de ne pas utiliser les frameworks et / ou les réflexions dans vos tests.
la source
Eh bien, cela semble être une discussion polémique. La première chose à laquelle il faut remédier est que l’ injection sur site est différente de l’injection Setter . Je travaille déjà avec certaines personnes qui pensent que l'injection de Field and Setter est la même chose.
Donc, pour être clair:
Injection sur le terrain:
Setter injection:
Mais, pour moi, ils partagent les mêmes préoccupations. Et, mon préféré, l'injection de constructeur:
J'ai les raisons suivantes de croire que l'injection de constructeur est meilleure que l'injection de setter / field (je citerai quelques liens Spring pour illustrer mon propos):
@AutoWired
votre code étant répandu, votre code dépend moins du framework. Je sais que la possibilité de changer simplement le cadre de l'ID dans un projet ne justifie pas cela (qui fait cela, non?). Mais cela montre, pour moi, un meilleur code (je l’ai appris de manière difficile avec les EJB et les recherches). Et avec Spring, vous pouvez même supprimer l'@AutoWired
annotation à l'aide de l'injection de constructeur.Si vous recherchez plus d'informations, je vous recommande cet article ancien (mais toujours pertinent) de Spring Blog nous expliquant pourquoi ils utilisent autant d'injection de setter et la recommandation d'utiliser l'injection de constructeur:
Et cette note sur la raison pour laquelle ils croient que le constructeur est plus approprié pour le code d'application:
Dernières pensées
Si vous n'êtes pas vraiment sûr de l'avantage du constructeur, peut-être que l'idée de mélanger un setter (pas de terrain) avec une injection de constructeur est une option, comme l'a expliqué l' équipe de Spring :
Mais rappelez-vous que l'injection sur le terrain est probablement celle à éviter parmi les trois.
la source
Votre dépendance est-elle susceptible de changer pendant la durée du cours? Si c'est le cas, utilisez l'injection de champ / propriété. Sinon, utilisez l'injection de constructeur.
la source