Fermer

septembre 22, 2021

Un point de vue abstrait sur le modèle d'injection de dépendance


Cet article jettera un regard relativement abstrait sur le modèle de programmation structurelle appelé 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–> IntfImpl

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 :

  1. L'injection de dépendances découple les implémentations des classes des implémentations des dépendances de ces classes.

  2. 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, Containerplutôt que Clscontrô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 Clset 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 Clsla 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 l'article de Martin Fowler sur l'injection de dépendances.

À propos de l'auteur

Ross Grogan-Kaylor est consultant technique associé au bureau de Minneapolis de Perficient. Il aime s'intéresser aux modèles structurels dans la syntaxe et dans les idées de haut niveau du développement logiciel.

Plus de cet auteur






Source link