Je comprends les avantages de l'injection de dépendance elle-même. Prenons le printemps par exemple. Je comprends également les avantages d'autres fonctionnalités de Spring comme AOP, des aides de différents types, etc. Je me demande simplement quels sont les avantages de la configuration XML tels que:
<bean id="Mary" class="foo.bar.Female">
<property name="age" value="23"/>
</bean>
<bean id="John" class="foo.bar.Male">
<property name="girlfriend" ref="Mary"/>
</bean>
par rapport à l'ancien code java tel que:
Female mary = new Female();
mary.setAge(23);
Male john = new Male();
john.setGirlfriend(mary);
qui est plus facile à déboguer, à vérifier le temps de compilation et peut être compris par quiconque ne connaît que java. Alors, quel est le but principal d'un framework d'injection de dépendances? (ou un morceau de code qui montre ses avantages.)
MISE À JOUR:
En cas de
IService myService;// ...
public void doSomething() {
myService.fetchData();
}
Comment le framework IoC peut-il deviner quelle implémentation de myService je veux être injectée s'il y en a plus d'une? S'il n'y a qu'une seule implémentation d'une interface donnée et que je laisse le conteneur IoC décider automatiquement de l'utiliser, il sera cassé après l'apparition d'une deuxième implémentation. Et s'il n'y a intentionnellement qu'une seule implémentation possible d'une interface, vous n'avez pas besoin de l'injecter.
Ce serait vraiment intéressant de voir un petit morceau de configuration pour IoC qui montre ses avantages. J'utilise Spring depuis un moment et je ne peux pas fournir un tel exemple. Et je peux montrer des lignes simples qui démontrent les avantages de la mise en veille prolongée, du dwr et d'autres frameworks que j'utilise.
MISE À JOUR 2:
Je me rends compte que la configuration IoC peut être modifiée sans recompilation. Est-ce vraiment une si bonne idée? Je peux comprendre quand quelqu'un veut changer les informations d'identification de la base de données sans recompiler - il n'est peut-être pas développeur. Dans votre pratique, à quelle fréquence quelqu'un d'autre que le développeur modifie la configuration IoC? Je pense que pour les développeurs, il n'y a aucun effort pour recompiler cette classe particulière au lieu de changer la configuration. Et pour les non-développeurs, vous voudrez probablement lui rendre la vie plus facile et fournir un fichier de configuration plus simple.
MISE À JOUR 3:
Configuration externe du mappage entre les interfaces et leurs implémentations concrètes
Qu'est-ce qui est si bon pour le rendre extenal? Vous ne rendez pas tout votre code externe, alors que vous le pouvez certainement - placez-le simplement dans le fichier ClassName.java.txt, lisez et compilez manuellement à la volée - wow, vous avez évité de recompiler. Pourquoi éviter la compilation?!
Vous gagnez du temps de codage car vous fournissez des mappages de manière déclarative, pas dans un code procédural
Je comprends qu'une approche parfois déclarative fait gagner du temps. Par exemple, je déclare une seule fois un mappage entre une propriété de bean et une colonne de base de données et hibernate utilise ce mappage lors du chargement, de l'enregistrement, de la construction de SQL basé sur HSQL, etc. C'est là que fonctionne l'approche déclarative. Dans le cas de Spring (dans mon exemple), la déclaration avait plus de lignes et avait la même expressivité que le code correspondant. S'il y a un exemple où une telle déclaration est plus courte que le code - je voudrais le voir.
Le principe d'inversion du contrôle permet des tests unitaires faciles car vous pouvez remplacer les implémentations réelles par de fausses (comme le remplacement d'une base de données SQL par une base de données en mémoire)
Je comprends l'inversion des avantages du contrôle (je préfère appeler le modèle de conception discuté ici comme injection de dépendance, car l'IoC est plus général - il existe de nombreux types de contrôle et nous n'en inversons qu'un seul - le contrôle de l'initialisation). Je demandais pourquoi quelqu'un a jamais besoin d'autre chose qu'un langage de programmation pour cela. Je peux certainement remplacer de vraies implémentations par de fausses en utilisant du code. Et ce code exprimera la même chose que la configuration - il initialisera simplement les champs avec de fausses valeurs.
mary = new FakeFemale();
Je comprends les avantages de la DI. Je ne comprends pas quels avantages sont ajoutés par la configuration XML externe par rapport à la configuration de code qui fait de même. Je ne pense pas que la compilation devrait être évitée - je compile tous les jours et je suis toujours en vie. Je pense que la configuration de DI est un mauvais exemple d'approche déclarative. La déclaration peut être utile si elle est déclarée une fois ET est utilisée plusieurs fois de différentes manières - comme hibernate cfg, où le mappage entre la propriété du bean et la colonne DB est utilisé pour enregistrer, charger, créer des requêtes de recherche, etc. La configuration Spring DI peut être facilement traduite en configurer le code, comme au début de cette question, n'est-ce pas? Et il n'est utilisé que pour l'initialisation du bean, n'est-ce pas? Ce qui veut dire qu'une approche déclarative n'ajoute rien ici, n'est-ce pas?
Lorsque je déclare le mappage d'hibernation, je donne simplement quelques informations à l'hibernation, et cela fonctionne en fonction de cela - je ne lui dis pas quoi faire. En cas de printemps, ma déclaration dit exactement au printemps ce qu'il faut faire - alors pourquoi le déclarer, pourquoi ne pas le faire?
DERNIÈRE MISE À JOUR: Les
gars, beaucoup de réponses me parlent de l'injection de dépendance, ce que JE SAIS EST BON. La question concerne le but de la configuration DI au lieu de l'initialisation du code - j'ai tendance à penser que l'initialisation du code est plus courte et plus claire. La seule réponse que j'ai eue jusqu'ici à ma question, c'est qu'elle évite la recompilation, lorsque la configuration change. Je suppose que je devrais poster une autre question, car c'est un grand secret pour moi, pourquoi la compilation devrait être évitée dans ce cas.
la source
Réponses:
Pour moi, l'une des principales raisons d'utiliser un IoC (et d'utiliser une configuration externe) concerne les deux domaines suivants:
Essai
Si vous divisez vos tests en 3 scénarios (ce qui est assez normal dans le développement à grande échelle):
Ce que vous voudrez faire, c'est pour les deux derniers scénarios de test (intégration et boîte noire), ne recompiler aucune partie de l'application.
Si l'un de vos scénarios de test vous oblige à modifier la configuration (par exemple: utiliser un autre composant pour imiter une intégration bancaire, ou effectuer une charge de performance), cela peut être facilement géré (cela présente les avantages de la configuration du côté DI d'un IoC cependant.
De plus, si votre application est utilisée sur plusieurs sites (avec une configuration de serveur et de composant différente) ou a une configuration changeante sur l'environnement en direct, vous pouvez utiliser les étapes ultérieures de test pour vérifier que l'application gérera ces modifications.
Production
En tant que développeur, vous n'avez pas (et ne devriez pas) avoir le contrôle de l'environnement de production (en particulier lorsque votre application est distribuée à plusieurs clients ou à des sites distincts), c'est pour moi le réel avantage d'utiliser à la fois une configuration IoC et externe. , car il appartient à l'infrastructure / support de production de peaufiner et d'ajuster l'environnement en direct sans avoir à retourner chez les développeurs et à travers des tests (coût plus élevé quand tout ce qu'ils veulent faire est de déplacer un composant).
Résumé
Les principaux avantages de la configuration externe d'un IoC proviennent du fait de donner à d'autres (non-développeurs) le pouvoir de configurer votre application, d'après mon expérience, cela n'est utile que dans un ensemble limité de circonstances:
En pratique, j'ai constaté que même lorsque vous développez quelque chose sur lequel vous avez le contrôle sur l'environnement sur lequel il sera exécuté, il est préférable au fil du temps de donner à quelqu'un d'autre les capacités de modifier la configuration:
Remarque: Application fait référence à la solution complète (pas seulement à l'exécutable), donc à tous les fichiers nécessaires à l'exécution de l'application .
la source
L'injection de dépendance est un style de codage qui trouve ses racines dans l'observation que la délégation d'objet est généralement un modèle de conception plus utile que l'héritage d'objet (c'est-à-dire que l'objet a une relation est plus utile que l'objet est une relation). Un autre ingrédient est cependant nécessaire pour que DI fonctionne, celui de créer des interfaces objets. En combinant ces deux modèles de conception puissants, les ingénieurs en logiciel ont rapidement réalisé qu'ils pouvaient créer un code flexible faiblement couplé et ainsi est né le concept d'injection de dépendance. Cependant, ce n'est que lorsque la réflexion d'objet est devenue disponible dans certains langages de haut niveau que la DI a vraiment pris son envol. La composante réflexion est au cœur de la plupart des jours d'aujourd'hui '
Un langage doit fournir une bonne prise en charge des techniques de programmation orientées objet normales ainsi que la prise en charge des interfaces d'objets et de la réflexion d'objets (par exemple Java et C #). Bien que vous puissiez créer des programmes en utilisant des modèles DI dans les systèmes C ++, son manque de prise en charge de la réflexion dans le langage proprement dit l'empêche de prendre en charge les serveurs d'applications et d'autres plates-formes DI et limite par conséquent l'expressivité des modèles DI.
Points forts d'un système construit à partir de modèles DI:
Le code DI semble définitivement plus lourd, les inconvénients d'avoir tous ces fichiers XML qui configurent des objets à injecter dans d'autres objets semblent difficiles. C'est cependant le but des systèmes DI. Votre capacité à mélanger et à faire correspondre des objets de code sous la forme d'une série de paramètres de configuration vous permet de créer des systèmes complexes en utilisant du code tiers avec un codage minimal de votre part.
L'exemple fourni dans la question touche simplement à la surface de la puissance expressive qu'une bibliothèque d'objets DI correctement factorisée peut fournir. Avec un peu de pratique et beaucoup d'autodiscipline, la plupart des praticiens en DI trouvent qu'ils peuvent construire des systèmes qui ont une couverture de test à 100% du code d'application. Ce seul point est extraordinaire. Il ne s'agit pas d'une couverture de test à 100% d'une petite application de quelques centaines de lignes de code, mais d'une couverture de test à 100% d'applications comprenant des centaines de milliers de lignes de code. Je ne suis pas en mesure de décrire tout autre modèle de conception qui offre ce niveau de testabilité.
Vous avez raison en ce sens qu'une application de seulement 10 lignes de code est plus facile à comprendre que plusieurs objets plus une série de fichiers de configuration XML. Cependant, comme avec les modèles de conception les plus puissants, les gains sont constatés à mesure que vous continuez à ajouter de nouvelles fonctionnalités au système.
En bref, les applications basées sur DI à grande échelle sont à la fois plus faciles à déboguer et à comprendre. Bien que la configuration Xml ne soit pas «vérifiée au moment de la compilation», tous les services d'application dont cet auteur a connaissance fourniront au développeur des messages d'erreur s'ils tentent d'injecter un objet ayant une interface incompatible dans un autre objet. Et la plupart fournissent une fonction de «vérification» qui couvre toutes les configurations d'objets connus. Cela se fait facilement et rapidement en vérifiant que l'objet A à injecter implémente l'interface requise par l'objet B pour toutes les injections d'objets configurés.
la source
C'est un peu une question chargée, mais j'ai tendance à convenir que d'énormes quantités de configuration xml ne représentent pas vraiment beaucoup d'avantages. J'aime que mes applications soient aussi légères que possible sur les dépendances, y compris les frameworks lourds.
Ils simplifient le code la plupart du temps, mais ils ont aussi une surcharge de complexité qui rend la recherche de problèmes assez difficile (j'ai vu de tels problèmes de première main, et Java simple avec lequel je serais beaucoup plus à l'aise).
Je suppose que cela dépend un peu du style, et de ce avec quoi vous êtes à l'aise ... aimez-vous piloter votre propre solution et avoir l'avantage de la connaître à fond, ou miser sur des solutions existantes qui peuvent s'avérer difficiles lorsque la configuration n'est pas t juste pas? C'est tout un compromis.
Cependant, la configuration XML est un peu une bête noire de la mienne ... J'essaye de l'éviter à tout prix.
la source
Chaque fois que vous pouvez modifier votre code en données, vous faites un pas dans la bonne direction.
Coder quoi que ce soit sous forme de données signifie que votre code lui-même est plus général et réutilisable. Cela signifie également que vos données peuvent être spécifiées dans une langue qui leur correspond exactement.
En outre, un fichier XML peut être lu dans une interface graphique ou un autre outil et facilement manipulé de manière pragmatique. Comment feriez-vous cela avec l'exemple de code?
Je factorise constamment les choses que la plupart des gens implémenteraient sous forme de code dans les données, cela rend le code qui reste BEAUCOUP plus propre. Je trouve inconcevable que les gens créent un menu en code plutôt qu'en tant que données - il devrait être évident que le faire dans le code est tout simplement faux à cause du passe-partout.
la source
La raison de l'utilisation d'un conteneur DI est que vous n'avez pas besoin d'avoir un milliard de propriétés préconfigurées dans votre code qui sont simplement des getters et des setters. Voulez-vous vraiment coder en dur tous ceux avec le nouveau X ()? Bien sûr, vous pouvez avoir une valeur par défaut, mais le conteneur DI permet la création de singletons, ce qui est extrêmement facile et vous permet de vous concentrer sur les détails du code, pas sur la tâche diverse de l'initialiser.
Par exemple, Spring vous permet d'implémenter l'interface InitializingBean et d'ajouter une méthode afterPropertiesSet (vous pouvez également spécifier une «méthode init» pour éviter de coupler votre code à Spring). Ces méthodes vous permettront de vous assurer que toute interface spécifiée en tant que champ dans votre instance de classe est correctement configurée au démarrage, et vous n'aurez plus à vérifier null vos getters et setters (en supposant que vous autorisez vos singletons à rester thread-safe ).
De plus, il est beaucoup plus facile de faire des initialisations complexes avec un conteneur DI au lieu de les faire vous-même. Par exemple, j'aide à utiliser XFire (pas CeltiXFire, nous n'utilisons que Java 1.4). L'application utilisait Spring, mais elle utilisait malheureusement le mécanisme de configuration services.xml de XFire. Lorsqu'une collection d'éléments avait besoin de déclarer qu'elle contenait ZERO ou plusieurs instances au lieu d'une ou plusieurs instances, je devais remplacer une partie du code XFire fourni pour ce service particulier.
Certaines valeurs par défaut de XFire sont définies dans son schéma Spring beans. Donc, si nous utilisions Spring pour configurer les services, les beans auraient pu être utilisés. Au lieu de cela, ce qui s'est passé, c'est que j'ai dû fournir une instance d'une classe spécifique dans le fichier services.xml au lieu d'utiliser les beans. Pour ce faire, j'avais besoin de fournir le constructeur et de configurer les références déclarées dans la configuration XFire. Le vrai changement que j'avais besoin de faire exigeait que je surcharge une seule classe.
Mais, grâce au fichier services.xml, j'ai dû créer quatre nouvelles classes, en définissant leurs valeurs par défaut en fonction de leurs valeurs par défaut dans les fichiers de configuration Spring de leurs constructeurs. Si nous avions pu utiliser la configuration Spring, j'aurais pu simplement dire:
Au lieu de cela, cela ressemblait plus à ceci:
Le résultat net est donc que quatre classes Java supplémentaires, pour la plupart inutiles, ont dû être ajoutées à la base de code pour obtenir l'effet qu'une classe supplémentaire et des informations de conteneur de dépendances simples ont obtenu. Ce n'est pas "l'exception qui prouve la règle", c'est la règle ... gérer les bizarreries dans le code est beaucoup plus propre lorsque les propriétés sont déjà fournies dans un conteneur DI et que vous les modifiez simplement pour s'adapter à une situation particulière, ce qui arrive le plus souvent.
la source
J'ai ta réponse
Il y a évidemment des compromis dans chaque approche, mais les fichiers de configuration XML externalisés sont utiles pour le développement d'entreprise dans lequel les systèmes de construction sont utilisés pour compiler le code et non votre IDE. En utilisant le système de construction, vous voudrez peut-être injecter certaines valeurs dans votre code - par exemple la version de la construction (ce qui pourrait être pénible d'avoir à mettre à jour manuellement à chaque fois que vous compilez). La douleur est plus grande lorsque votre système de construction extrait le code d'un système de contrôle de version. La modification de valeurs simples au moment de la compilation vous obligerait à modifier un fichier, à le valider, à le compiler, puis à revenir à chaque fois pour chaque modification. Ce ne sont pas des modifications que vous souhaitez valider dans votre contrôle de version.
Autres cas d'utilisation utiles concernant le système de construction et les configurations externes:
Mise à jour: Tous les exemples ci-dessus concernaient des éléments qui ne nécessitaient pas nécessairement de dépendances sur les classes. Mais vous pouvez facilement créer des cas où un objet complexe et une automatisation sont nécessaires - par exemple:
la source
Vous n'avez pas besoin de recompiler votre code chaque fois que vous modifiez quelque chose dans la configuration. Cela simplifiera le déploiement et la maintenance du programme. Par exemple, vous pouvez échanger un composant avec un autre avec seulement 1 changement dans le fichier de configuration.
la source
Vous pouvez insérer une nouvelle implémentation pour petite amie. Ainsi, une nouvelle femelle peut être injectée sans recompiler votre code.
(Ce qui précède suppose que Female et HotFemale implémentent la même interface GirlfFriend)
la source
Dans le monde .NET, la plupart des frameworks IoC fournissent à la fois une configuration XML et Code.
StructureMap et Ninject, par exemple, utilisent des interfaces fluides pour configurer les conteneurs. Vous n'êtes plus obligé d'utiliser des fichiers de configuration XML. Spring, qui existe également dans .NET, repose fortement sur les fichiers XML car il s'agit de son interface de configuration principale historique, mais il est toujours possible de configurer des conteneurs par programme.
la source
Facilité de combiner des configurations partielles en une configuration finale complète.
Par exemple, dans les applications Web, le modèle, la vue et les contrôleurs sont généralement spécifiés dans des fichiers de configuration distincts. Utilisez l'approche déclarative, vous pouvez charger, par exemple:
Ou charger avec une interface utilisateur différente et quelques contrôleurs supplémentaires:
Faire la même chose dans le code nécessite une infrastructure pour combiner des configurations partielles. Pas impossible à faire dans le code, mais certainement plus facile à faire en utilisant un framework IoC.
la source
Souvent, le point important est de savoir qui modifie la configuration après l'écriture du programme. Avec la configuration dans le code, vous supposez implicitement que la personne qui le change a les mêmes compétences et accès au code source, etc. que l'auteur d'origine.
Dans les systèmes de production, il est très pratique d'extraire un sous-ensemble de paramètres (par exemple, l'âge dans votre exemple) dans un fichier XML et de permettre par exemple à l'administrateur système ou au personnel d'assistance de modifier la valeur sans leur donner le plein pouvoir sur le code source ou d'autres paramètres - ou simplement pour les isoler des complexités.
la source
Du point de vue du printemps, je peux vous donner deux réponses.
Premièrement, la configuration XML n'est pas le seul moyen de définir la configuration. La plupart des choses peuvent être configurées à l'aide d'annotations et les choses qui doivent être faites avec XML sont la configuration du code que vous n'écrivez pas de toute façon, comme un pool de connexions que vous utilisez à partir d'une bibliothèque. Spring 3 inclut une méthode pour définir la configuration DI à l'aide de Java similaire à la configuration DI roulée à la main dans votre exemple. Donc, utiliser Spring ne signifie pas que vous devez utiliser un fichier de configuration basé sur XML.
Deuxièmement, Spring est bien plus qu'un simple framework DI. Il possède de nombreuses autres fonctionnalités, notamment la gestion des transactions et l'AOP. La configuration Spring XML mélange tous ces concepts. Souvent, dans le même fichier de configuration, je spécifie des dépendances de bean, des paramètres de transaction et j'ajoute des beans à portée de session qui géraient réellement l'utilisation d'AOP en arrière-plan. Je trouve que la configuration XML offre un meilleur endroit pour gérer toutes ces fonctionnalités. Je pense également que la configuration basée sur les annotations et la configuration XML évoluent mieux que la configuration basée sur Java.
Mais je comprends votre point de vue et il n'y a rien de mal à définir la configuration d'injection de dépendances en Java. Je le fais normalement moi-même dans les tests unitaires et lorsque je travaille sur un projet suffisamment petit pour que je n'ai pas ajouté de framework DI. Je ne spécifie normalement pas la configuration en Java parce que pour moi, c'est le genre de code de plomberie que j'essaie d'éviter d'écrire lorsque j'ai choisi d'utiliser Spring. C'est une préférence cependant, cela ne signifie pas que la configuration XML est supérieure à la configuration basée sur Java.
la source
Spring a également un chargeur de propriétés. Nous utilisons cette méthode pour définir des variables dépendant de l'environnement (ex: développement, test, acceptation, production, ...). Cela peut être par exemple la file d'attente à écouter.
S'il n'y a aucune raison pour laquelle la propriété changerait, il n'y a pas non plus de raison de la configurer de cette manière.
la source
Votre cas est très simple et n'a donc pas besoin d'un conteneur IoC (Inversion of Control) comme Spring. D'un autre côté, lorsque vous "programmez sur des interfaces, pas sur des implémentations" (ce qui est une bonne pratique en POO), vous pouvez avoir un code comme celui-ci:
(notez que le type de myService est IService - une interface, pas une implémentation concrète). Désormais, il peut être pratique de laisser votre conteneur IoC fournir automatiquement l'instance concrète correcte d'IService lors de l'initialisation - lorsque vous avez de nombreuses interfaces et de nombreuses implémentations, il peut être fastidieux de le faire manuellement. Les principaux avantages d'un conteneur IoC (framework d'injection de dépendances) sont:
la source
L'initialisation dans un fichier de configuration XML simplifiera votre travail de débogage / d'adaptation avec un client qui a déployé votre application sur ses ordinateurs. (Parce qu'il ne nécessite pas de recompilation + remplacement de fichiers binaires)
la source
L'une des raisons les plus attrayantes est le " principe hollywoodien ": ne nous appelez pas, nous vous appellerons. Un composant n'est pas obligé d'effectuer lui-même les recherches vers d'autres composants et services; au lieu de cela, ils lui sont fournis automatiquement. En Java, cela signifie qu'il n'est plus nécessaire d'effectuer des recherches JNDI à l'intérieur du composant.
Il est également beaucoup plus facile de tester un composant isolément: au lieu de lui donner une implémentation réelle des composants dont il a besoin, vous utilisez simplement des simulations (éventuellement générées automatiquement).
la source