injection de dépendance ou inversion de contrôle. Je pense que la plupart des articles sur l'injection de dépendances s'enlisent trop dans les détails de l'exemple utilisé pour démontrer la structure. Dans cet article, nous allons présenter l'abstraction pure.
Considérons la situation de dépendance suivante, dans laquelle une classe Cls
dépend à la fois d'une interface Intf
et d'une implémentation Impl
de cette interface.
interface publique Intf { void helperMethod(Object args); } la classe publique Impl étend Intf { @Passer outre public void helperMethod(Object args) { /* implémentation */ } } classe publique Cls { méthode void publique (arguments d'objet) { Intf intf = new Impl(); intf.helperMethod(args); } }
Cls
dépend de Impl
car il crée un objet Impl
en exécutant new Impl()
. Pour reformuler, notre situation de dépendance actuelle est :
Cls
–creates–> Intf
Impl
Impl
–is–> Intf
Nous voulons une situation de dépendance dans laquelle Cls
dépend uniquement de Intf
et non de Impl
. C'est-à-dire que nous voulons découpler l'implémentation de Cls
de toute implémentation particulière de Intf
.
Pour y parvenir, nous passerons la responsabilité de créer [19659007]Impl à une classe Container
qui gère Cls
; ainsi, Container
« injectera » Impl
dans Cls
en utilisant le constructeur public with-arguments de Cls
. (Cela signifie que Cls
doit en fait avoir un constructeur public avec arguments). Cette configuration est appelée injection de dépendances. Si nous implémentons l'injection de dépendances décrite précédemment, nous nous trouvons dans la situation de dépendance suivante :
Cls
–has–> Intf
Container
–has–> Cls
Conteneur
–a–> Intf
Conteneur
–crée–> Impl
Impl
–est–> [19659007]Intf.
En plus de résoudre le problème de découplage, l'injection de dépendances présente un avantage si extravagant que la plupart la considèrent comme « le but » de l'injection de dépendances. L'avantage est le suivant : dans le cas où il est logique que plusieurs instances de Cls
s'appuient sur la même implémentation Intf
(par exemple, différentes classes s'appuyant sur un objet d'accès à la base de données), [19659007]Container peut être configuré pour gérer l'implémentation de Intf
pour chaque classe. Ainsi, notre nouvelle situation de dépendance permet le partage d'une instance Intf
entre plusieurs instances de Cls
.
Summary
Pour résumer, voici les deux principaux avantages de la dépendance injection :
-
L'injection de dépendances découple les implémentations des classes des implémentations des dépendances de ces classes.
-
Dans l'injection de dépendances, une classe « conteneur » gère les dépendances de toutes les autres classes. (Ainsi, ces « autres classes » n'utilisent pas d'instructions
new
pour créer leurs propres dépendances). Cela permet le partage d'une seule dépendance entre plusieurs classes, le cas échéant.
Voici le code qui implémente le modèle d'injection de dépendance.
public interface Intf { void helperMethod(Object args); } la classe publique Impl étend Intf { arguments d'objet privés ; public Impl(Object args) { this.args = args; } @Passer outre public void helperMethod() { /* implémentation qui utilise this.args */ } } classe publique Cls { intf privé; public Cls(Intf intf) { this.intf = intf; } public void method() { intf.helperMethod(); } } Public class Container{ /* l'implémentation sera un peu compliquée */ } classe publique principale { /* Un fichier de configuration effectue généralement la tâche de cette méthode. */ Conteneur privé configureContainer() { Object args = ... // obtient les arguments qui doivent être passés au constructeur Impl Conteneur cntr = new Conteneur(); /* La ligne ci-dessous indique à cntr toutes les informations dont il a besoin pour exécuter l'instruction "Intf intf = Impl(args)". */ cntr.registerComponentImplementation(Intf.class, Impl.class, args); /* Cette ligne suivante indique à cntr toutes les informations dont il a besoin pour exécuter l'instruction "Cls cls = nouveau Cls(intf)". */ cntr.registerComponentImplementation(Cls.class); retour cntr; } public static void main(String[] _args) { /* C'est ainsi que nous appelons cls.method(). */ Conteneur cntr = configureContainer(); Cls cls = (Cls) cntr.getComponentInstance(Cls.class); cls.method(); // Ceci exécute la même tâche que "cls.method(args)" dans la situation d'origine. } }
Une dernière remarque. Puisque, dans l'injection de dépendances, Container
plutôt que Cls
contrôle quoi et quand les dépendances de Cls
sont injectées, le contrôle a en quelque sorte été inversé. L'injection de dépendance est donc souvent appelée inversion de contrôleet une classe telle que Container
est souvent appelée inversion de contrôle conteneurou [19659114]Conteneur IoC.
Un particulier concernant les lignes impliquant cntr.registerComponentImplementation()
n'était pas immédiatement clair pour moi, et pourrait donc être source de confusion pour vous aussi. Concrètement, est-il nécessaire de passer Intf.class
au premier appel de registerComponentImplementation()
? Il semble qu'il devrait exister une implémentation de Container
telle que si nous exécutons ce qui suit, cntr
fait ce que nous attendions en coulisse.
cntr.registerComponentImplementation(Impl.class , arguments); cntr.registerComponentImplementation(Cls.class);
C'est-à-dire qu'il semble que cntr
aurait suffisamment d'informations pour faire new Cls(new Impl(args))
. En effet, cntr
a un Cls
et Cls
sait que Impl
est un Intf
. Et, pour les besoins de l'argument, même si nous supposons que cntr
d'une manière ou d'une autre n'était pas au courant de Cls
la JVM elle-même sait que Impl
est un Intf
– après tout, l'exécution de new Cls(new Impl(args))
ne nécessite pas que nous tapions cast new Impl(args)[19659008]à
Intf
!
Réponse : après enquête, j'ai découvert qu'il était juste de transmettre plus d'informations que ce qui est strictement nécessaire dans certains frameworks d'injection de dépendances.
Source
La structure de la classe Main
dans ce qui précède provient d'un exemple donné dans
Source link