Pourquoi Inversion of Control est-il nommé ainsi?

98

Les mots invertou controlne sont pas utilisés du tout pour définir Inversion of Control dans les définitions que j'ai vues.

Définitions

Wikipédia

inversion of control (IoC) est une technique de programmation, exprimée ici en termes de programmation orientée objet, dans laquelle le couplage d'objet est lié au moment de l'exécution par un objet assembleur et n'est généralement pas connu au moment de la compilation à l'aide d'une analyse statique. ~ http://en.wikipedia.org/wiki/Inversion_of_control

Martin Fowler

L'inversion de contrôle est un modèle courant dans la communauté Java qui aide à câbler des conteneurs légers ou à assembler des composants de différents projets dans une application cohérente. ~ basé sur http://www.martinfowler.com/articles/injection.html (reformulé)


Alors, pourquoi Inversion of Control est-il appelé Inversion of Control? Quel contrôle est inversé et par quoi? Existe-t-il un moyen de définir l'inversion de contrôle à l'aide de la terminologie: inverser et contrôler ?

Korey Hinton
la source
22
Des accessoires pour la personne qui peut expliquer cela en anglais clair.
Robert Harvey
2
Si une définition utilisait le mot qu'elle définissait, cela constituerait un échec du dictionnaire. Une logique similaire s'applique ici.
Izkata
2
Voir aussi sur StackOverflow: Qu'est-ce que Inversion of Control?
Izkata
2
@Izkata, il est très courant que les dictionnaires définissent des expressions en termes de termes propres à la phrase ou de termes synonymes. c'est-à-dire que la définition de la force de la nature utilise les mots force et nature dans sa définition ou que les termes du service, la définition utilise les mots règles et service, où règles est synonyme de termes.
Korey Hinton
3
Je préfère utiliser le terme "injection automatisée de dépendance" ou ADI au lieu de IoC, pour cette raison même; Fowler lui-même mentionne que la plupart des paradigmes actuels en programmation ont une sorte "d'inversion de contrôle", où ce terme est généralement défini comme "la reconstitution des déterminations du code à exécuter et l'heure à laquelle il doit être exécuté système de contrôle, lorsque de telles déterminations sont traditionnellement effectuées par le programme lui-même ". Tout, des interruptions matérielles à la programmation événementielle en passant par les machines virtuelles et le multitâche se produit avec IoC.
KeithS

Réponses:

67

Supposons que vous ayez une sorte de classe "référentiel" et que ce dernier soit responsable de la gestion des données depuis une source de données.

Le référentiel pourrait établir une connexion à la source de données par lui-même. Mais que se passe-t-il si cela vous permettait de passer une connexion à la source de données via le constructeur du référentiel?

En autorisant l'appelant à établir la connexion, vous avez dissocié la dépendance de la connexion à la source de données de la classe de référentiel, permettant ainsi à toute source de données de fonctionner avec le référentiel, et pas uniquement celle spécifiée par le référentiel.

Vous avez inversé le contrôle en confiant la responsabilité de la création de la connexion de la classe de référentiel à l'appelant.

Martin Fowler suggère d'utiliser le terme "Injection de dépendance" pour décrire ce type d'inversion de contrôle, étant donné que le concept d'inversion de contrôle peut être appliqué plus largement que la simple injection de dépendances dans une méthode constructeur.

Robert Harvey
la source
3
Ceci est un exemple d'injection de dépendance, mais l'injection de dépendance n'est qu'un sous-ensemble d'inversion de contrôle. Il y a d'autres choses totalement sans rapport avec l'injection de dépendance qui pratiquent l'inversion de contrôle.
Dsw88
1
@ mustang2009cobra: Merci. Un exemple est ce que je recherchais, pas un catalogue exhaustif d'instances dans lesquelles cela se produit, et le terme "Inversion of Control" est plus étroitement associé à DI, pas à la transmission de messages ou à d'autres concepts similaires.
Robert Harvey
Il est vrai que l’injection de dépendance est l’exemple le plus courant d’IoC. C’est pourquoi un exemple d’ID est le plus logique. Peut-être un petit changement dans votre réponse en spécifiant qu'il s'agit d'un exemple DI, qui est le type le plus important d'IoC?
Dsw88
1
Ceci est exactement ce que je cherchais! Un grand nombre d'exemples et de définitions d'IoC ne spécifient pas clairement quel contrôle est inversé. Je me rends compte que l'IoC peut être plus qu'une simple injection de dépendance, mais maintenant quelque chose a finalement cliqué dans mon cerveau. Merci!
Korey Hinton
79

Je pense que personne ne peut mieux l'expliquer que Martin Fowler, plus loin dans l'article que vous avez lié .

Pour cette nouvelle génération de conteneurs, l'inversion concerne la façon dont ils recherchent une implémentation de plug-in. Dans mon exemple naïf, le lister a cherché l'implémentation du finder en l'instanciant directement. Cela empêche le chercheur d'être un plugin. L’approche utilisée par ces conteneurs consiste à s’assurer que tout utilisateur d’un plug-in respecte une convention permettant à un module assembleur distinct d’injecter l’implémentation dans le lister.

Comme il l'explique dans les paragraphes ci-dessus, ce n'est pas tout à fait la même raison que le terme "Inversion of Control".

Lorsque ces conteneurs parlent de leur utilité, car ils implémentent "Inversion of Control", je suis très perplexe. L'inversion de contrôle est une caractéristique commune des structures, donc dire que ces conteneurs légers sont spéciaux parce qu'ils utilisent l'inversion de contrôle revient à dire que ma voiture est spéciale parce qu'elle a des roues.

La question est: quel aspect du contrôle inversent-ils? La première fois que j'ai rencontré une inversion de contrôle, c'était dans le contrôle principal d'une interface utilisateur. Les premières interfaces utilisateur étaient contrôlées par le programme d'application. Vous auriez une séquence de commandes comme "Entrer le nom", "entrer l'adresse"; votre programme piloterait les invites et répondrait à chacune d'elles. Avec les interfaces utilisateur graphiques (ou même basées sur un écran), la structure d'interface utilisateur contenait cette boucle principale et votre programme fournissait plutôt des gestionnaires d'événements pour les différents champs de l'écran. Le contrôle principal du programme était inversé, déplacé de vous vers le cadre.

C’est pourquoi il invente le terme «injection de dépendance» pour couvrir cette implémentation spécifique de l’inversion du contrôle.

En conséquence, je pense que nous avons besoin d’un nom plus spécifique pour ce motif. Inversion of Control est un terme trop générique et les gens le trouvent donc déroutant. Suite à de nombreuses discussions avec divers défenseurs d'IoC, nous avons opté pour le nom de Dependency Injection.

Pour clarifier un peu: Inversion de contrôle signifie tout ce qui inverse la structure de contrôle d'un programme à partir de la conception procédurale classique.

Autrefois, un exemple clé consistait à laisser un cadre gérer la communication entre une interface utilisateur et votre code, plutôt que de laisser votre code générer directement l'interface utilisateur.

À une époque plus récente (lorsque de tels cadres dominaient à peu près, la question n'était donc plus d'actualité), un exemple consistait à inverser le contrôle sur l'instanciation d'objets.

Fowler et d’autres ont décidé que le terme Inversion of Control couvrait trop de techniques et que nous avions besoin d’un nouveau terme pour désigner un exemple spécifique d’instanciation d’objets (Dependency Injection), mais, au moment où cet accord avait été conclu, le terme "avait décollé.

Cela brouille beaucoup l'eau, car un conteneur IoC est un type spécifique d'injection de dépendance, mais l'injection de dépendance est un type spécifique d'inversion de contrôle. C'est pourquoi vous obtenez des réponses aussi confuses, peu importe où vous regardez.

pdr
la source
4
Normalement, je ne vote pas beaucoup, mais c'est une excellente réponse. 1 vote ne suffira pas. :(
RJD22
1
cette réponse répond vraiment à toute la question des confusions IoC et DI.
Ali Umair
32

Voici les programmes de flux de contrôle "normaux" habituellement suivis:

  • Exécuter les commandes séquentiellement
  • Vous gardez le contrôle sur le flux de contrôle du programme

L'inversion de contrôle "inverse" ce flux de contrôle, ce qui signifie qu'il le retourne sur sa tête:

  • Votre programme ne contrôle plus le flux. Plutôt que d'appeler des commandes comme bon vous semble, vous attendez que quelqu'un d'autre vous appelle .

Cette dernière ligne est la plus importante. Plutôt que d'appeler quelqu'un d'autre quand vous en avez envie, quelqu'un d'autre vous appelle quand ils en ont envie.

Un exemple courant est celui des frameworks Web tels que Rails. Vous définissez les contrôleurs, mais vous ne décidez pas quand ils seront appelés. Rails les appelle quand il le décide.

dsw88
la source
19
[insérer la blague "en soviétique" obligatoire ici]
Robert Harvey le
2
Blague à part, ce que je pense que vous décrivez est plus comme un message qui passe, qui est un aliment de base de OO depuis des décennies.
Robert Harvey
3
@ JimmyHoffa - Ce n'est pas faux du tout - voir ici . L'inversion de contrôle est un concept plus général que la création de dépendances, et l'injection de dépendance n'est qu'une forme d'IoC.
Lee
7
@ JimmyHoffa: Je ne suis pas d'accord. C’est autant une définition correcte de l’inversion de contrôle que votre réponse ou celle de Robert, ce qui correspond exactement à la confusion décrite par Fowler lors de la formulation de l’expression Dependency Injection (que vous appelez tous les deux IoC).
pdr
5
Je suis d'accord avec cette affirmation de @pdr: "Inversion de contrôle signifie tout ce qui inverse la structure de contrôle d'un programme à partir de la conception procédurale classique." C'est ce que je décris, c'est l'inversion générique du contrôle. L'injection de dépendance est un type d'IoC, mais ce n'est pas la seule chose qui pratique l'IoC
dsw88
13

Il s'agit de savoir qui contrôle l'instanciation des dépendances.

Traditionnellement, quand une classe / méthode doit utiliser une autre classe (dépendance), elle est instanciée directement par la classe / méthode. Il contrôle ses dépendances.

Avec Inversion of Control (IoC), l' appelant est passé dans la dépendance, d'où il instancie la dépendance. L'appelant contrôle les dépendances.

Le contrôle de l'endroit où une dépendance est instanciée a été inversé - au lieu d'être dans le "bas", où se trouve le code qui en a besoin, il est instancié dans le "haut", où le code qui en a besoin est appelé.

Oded
la source
1
Pas un anglais simple. Échouer.
Robert Harvey
2
@ RobertHarvey - à quel point la situation est-elle simple?
Oded
4
@ RobertHarvey Hein? Cela me semble assez simple ... Qu'est-ce qui ne l'est pas? Le mot "instanciation" ou "dépendances"?
Jimmy Hoffa
@ JimmyHoffa: Voir ma réponse, qui fournit un exemple spécifique. IoC est un de ces termes gênants que tout le monde utilise mais que personne n'explique. les gens utilisent des conteneurs IoC dans des programmes qui n'en ont pas besoin parce qu'ils se méprennent. Cela dit, je pense que cette réponse comportait quelques modifications de ninja. :)
Robert Harvey
Ceci et le message be @ dsw88 ci-dessus sont assez clairs pour moi. Écoutez, si je n'avais jamais entendu parler d'IoC, je penserais intuitivement "Oh, où est-il possible de contrôler quelque chose qui bascule d'un côté à l'autre". Bon travail à ce sujet!
Oliver Williams
2

Généralement, les appels de code de niveau supérieur (c.-à-d. Les commandes) code de niveau inférieur Main () appelle function (), function () appelle libraryFunction ().

Cela peut être inversé, ainsi la fonction de bibliothèque de bas niveau au bas appelle des fonctions de niveau supérieur.

Pourquoi ferais-tu ça? Middleware. Parfois, vous voulez contrôler le niveau le plus élevé et le niveau le plus bas, mais vous ne voulez pas trop travailler au milieu. Prenez l'implémentation de quicksort dans C stdlib . Vous appelez quicksort au plus haut niveau. Vous remettez à qsort () un pointeur de fonction sur votre propre fonction qui implémente un comparateur sur ce que vous souhaitez. Lorsque qsort () est appelé, il appelle cette fonction de comparateur. qsort () contrôle / appelle / pilote votre fonction de haut niveau.

Philippe
la source
1

Voici un aperçu simple:

  1. Le contrôle fait référence à ce que le programme fait ensuite
  2. Au niveau supérieur, il y a généralement deux choses qui contrôlent le contrôle: l'application elle-même et l'utilisateur.

Dans l'ancien temps, le contrôle appartenait d'abord à l'application, puis à l'utilisateur. Si l'application avait besoin de quelque chose de la part de l'utilisateur, elle s'arrêterait et demanderait, puis passerait à la tâche suivante. L'interaction de l'utilisateur fournissait principalement des données plutôt que de contrôler ce que l'application faisait ensuite. Cela nous est un peu étranger de nos jours car nous ne voyons pas ce type de comportement très souvent.

Si nous inversons la situation et donnons à l'utilisateur le contrôle principal, nous avons inversé le contrôle. Cela signifie qu'au lieu d'attendre que l'application lui donne quelque chose à faire, l'application attend que l'utilisateur lui donne quelque chose à faire. Les interfaces graphiques en sont un excellent exemple. Pratiquement tout ce qui a une boucle d’événement a un contrôle inversé.

Notez que mon exemple est au niveau supérieur et que ce concept d'inversion de contrôle peut être résumé à différentes couches de contrôle au sein de l'application (c'est-à-dire l'injection de dépendance). C'est peut-être pourquoi il est si difficile d'obtenir une réponse directe.

Loren
la source
0

Essayons de le comprendre à travers les deux exemples.

Exemple 1

Auparavant, les applications utilisées pour générer des invites de commande acceptaient les entrées utilisateur les unes après les autres. Aujourd'hui, les infrastructures d'interface utilisateur instancient divers éléments d'interface utilisateur, parcourent divers événements de ces éléments (comme le survol de la souris, le clic, etc.) et les programmes utilisateur / principaux fournissent des points d'ancrage (par exemple, des écouteurs d'événements d'interface utilisateur en Java) pour écouter ces événements. Ainsi, le "contrôle" principal du flux d'éléments d'interface utilisateur est déplacé du programme utilisateur vers le cadre de l'interface utilisateur. Dans les premiers jours, c'était dans le programme utilisateur.

Exemple 2

Considérez la classe CustomerProcessorci - dessous:

class CustomerProcessor
{
    SqlCustRepo custRepo = new SqlCustRepo(); 
    private void processCustomers()
    {
            Customers[] custs = custRepo.getAllCusts();
    }
}

Si je veux processCustomer()être indépendant de toute implémentation de getAllCusts(), pas seulement celle fournie par SqlCustRepo, je devrai me débarrasser de la ligne: SqlCustRepo custRepo = new SqlCustRepo()et la remplacer par quelque chose de plus générique, capable d'accepter différents types d'implémentations, de telle sorte que processCustomers()cela fonctionnera simplement pour toute mise en œuvre fournie. Le code ci-dessus (instanciation de la classe requise SqlCustRepopar la logique du programme principal) est une méthode traditionnelle et il n’atteint pas cet objectif de découplage processCustomers()de la mise en œuvre de getAllCusts(). En inversion de contrôle, le conteneur instancie la classe d'implémentation requise (spécifiée par, par exemple, configuration xml), l'injecte dans la logique de programme principale qui est liée selon les crochets spécifiés (par exemple, par @Autowiredannotation ou par getBean()méthode dans le cadre Spring).

Permet de voir comment cela peut être fait. Considérez ci-dessous le code.

Config.xml

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
    <bean id="custRepo" class="JsonCustRepo" />
</beans>

CustRepo.java

interface ICustRepo 
{ ... }

JsonCustRepo.java

class JsonCustRepo implements CustRepo
{ ... }

App.java

class App
{
    public static void main(String[] args) 
    {
        ApplicationContext context = new ClassPathXmlApplicationContext("Config.xml");
        ICustRepo custRepo = (JsonCustRepo) context.getBean("custRepo");
    }
}

Nous pouvons aussi avoir

class GraphCustRepo implements ICustRepo { ... }   

et

<bean id="custRepo" class="GraphCustRepo">

et nous n'aurons pas besoin de changer App.java.

Au-dessus du conteneur (qui est le framework Spring), il incombe d'analyser le fichier xml, d'instancier le bean d'un type spécifique et de l'injecter dans le programme utilisateur. Le programme utilisateur n'a aucun contrôle sur la classe instanciée.

PS: IoC est un concept générique et est atteint à bien des égards. Les exemples ci-dessus y parviennent par injection de dépendance.

Référence: article de Martin Fowler .

Mahesha999
la source