web-dev-qa-db-fra.com

Pourquoi l'inversion du contrôle est-elle ainsi nommée?

Les mots invert ou control ne sont pas du tout utilisés pour définir l'inversion de contrôle dans les définitions que j'ai vues.

Définitions

Wikipédia

l'inversion de contrôle (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 du 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 l'inversion de contrôle est-elle appelée inversion de contrôle? Quel contrôle est inversé et par quoi? Existe-t-il un moyen de définir l'inversion de contrôle en utilisant la terminologie: inverser et contrôle?

102
Korey Hinton

Supposons que vous ayez une sorte de classe de "référentiel" et que ce référentiel soit chargé de vous transmettre des données à partir d'une source de données.

Le référentiel peut établir lui-même une connexion à la source de données. Mais que se passe-t-il s'il vous permet de passer une connexion à la source de données via le constructeur du référentiel?

En permettant à l'appelant de fournir la connexion, vous avez découplé la dépendance de connexion à la source de données de la classe de référentiel, permettant à la source de données any de fonctionner avec le référentiel, et pas seulement celle spécifiée par le référentiel.

Vous avez contrôle inversé en transférant la responsabilité de créer 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, car l'inversion de contrôle en tant que concept peut être appliquée plus largement que la simple injection de dépendances dans une méthode constructeur.

68
Robert Harvey

Je ne pense pas que quiconque puisse l'expliquer mieux que Martin Fowler, plus bas l'article auquel vous avez lié .

Pour cette nouvelle race de conteneurs, l'inversion concerne la façon dont ils recherchent une implémentation de plugin. Dans mon exemple naïf, le listeur a recherché l'implémentation du Finder en l'instanciant directement. Cela empêche le Finder d'être un plugin. L'approche utilisée par ces conteneurs est de garantir que tout utilisateur d'un plugin suit une convention qui permet à 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 de contrôle" est né.

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

La question est: quel aspect du contrôle inversent-ils? Lorsque j'ai rencontré l'inversion de contrôle pour la première fois, 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 "Entrez le nom", "entrez l'adresse"; votre programme piloterait les invites et prendrait une réponse à chacun. Avec des interfaces graphiques (ou même basées sur l'écran), le cadre de l'interface utilisateur contiendrait cette boucle principale et votre programme aurait plutôt fourni des gestionnaires d'événements pour les différents champs de l'écran. Le contrôle principal du programme a été inversé, éloigné de vous au cadre.

C'est pourquoi il continue à inventer le terme "Dependency Injection" pour couvrir cette implémentation spécifique d'Inversion of Control.

Par conséquent, je pense que nous avons besoin d'un nom plus spécifique pour ce modèle. Inversion de contrôle est un terme trop générique, et donc les gens le trouvent déroutant. En conséquence, avec de nombreuses discussions avec divers défenseurs de l'IoC, nous avons choisi le nom Dependency Injection.

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

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

Plus récemment (lorsque de tels frameworks dominaient à peu près, la question n'était donc plus pertinente), un exemple était l'inversion du contrôle sur l'instanciation des objets.

Fowler et d'autres ont décidé que le terme Inversion of Control couvrait trop de techniques et nous avions besoin d'un nouveau terme pour l'exemple spécifique d'instanciation d'objets (Dependency Injection) mais, au moment où cet accord avait été conclu, la phrase "IoC Container "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 si confuses, peu importe où vous regardez.

80
pdr

Voici les programmes de flux de contrôle "réguliers" généralement suivis:

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

L'inversion du contrôle "inverse" ce contrôle du flux, ce qui signifie qu'il le renverse 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 il en a envie.

Un exemple courant de ceci est les frameworks web tels que Rails. Vous définissez des contrôleurs, mais vous ne décidez pas vraiment quand ceux-ci seront appelés. Rails les appelle quand il décide qu'il y a un besoin.

32
dsw88

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

Traditionnellement, lorsqu'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 l'inversion de contrôle (IoC), le appelant passé dans la dépendance, donc 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 en "bas", où se trouve le code qui en a besoin, il est instancié en "haut", où le code qui en a besoin est appelé.

13
Oded

En règle générale, les appels de code de niveau supérieur (c.-à-d. Les commandes) ont un code de niveau inférieur. Main () appelle function (), function () appelle libraryFunction ().

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

Pourquoi ferais-tu ça? Middleware. Parfois, vous voulez contrôler le niveau supérieur et le niveau inférieur, mais il y a beaucoup de travail au milieu que vous ne voulez tout simplement pas faire. Prenez l'implémentation de quicksort dans le C stdlib . Vous appelez quicksort au niveau supérieur. Vous remettez à qsort () un pointeur de fonction vers votre propre fonction qui implémente un comparateur sur ce que vous ressentez. Lorsque qsort () est appelé, il appelle cette fonction de comparateur. qsort () contrôle/appelle/pilote votre fonction de haut niveau.

2
Philip

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

Autrefois, le contrôle appartenait d'abord à l'application et à l'utilisateur en second. Si l'application avait besoin de quelque chose de l'utilisateur, elle s'arrêterait et demanderait, puis passerait à sa prochaine tâche. L'interaction de l'utilisateur a fourni principalement des données plutôt que de contrôler ce que l'application a fait ensuite. C'est un peu étranger pour nous de nos jours puisque nous ne voyons pas ce type de comportement très souvent.

Si nous inversons cela et donnons à l'utilisateur le contrôle principal, alors nous avons inversé le contrôle. Cela signifie qu'au lieu que l'utilisateur attende que l'application lui donne quelque chose à faire, l'application reste assise à attendre que l'utilisateur lui donne quelque chose à faire. Les interfaces graphiques sont un excellent exemple de cela et à peu près tout avec 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 abstrait pour 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 claire.

1
Loren

Essayons de le comprendre à travers les deux exemples.

Exemple 1

Auparavant, les applications généraient des invites de commande pour accepter les entrées utilisateur les unes après les autres. Aujourd'hui, les cadres d'interface utilisateur instancient divers éléments d'interface utilisateur, parcourent divers événements de ces éléments d'interface utilisateur (comme le survol de la souris, cliquez sur etc.) et les programmes utilisateur/principal fournissent des crochets (par exemple, des écouteurs d'événements d'interface utilisateur en Java) pour écouter ces événements. Ainsi, le "contrôle" principal du flux des éléments de l'interface utilisateur est déplacé du programme utilisateur vers le cadre de l'interface utilisateur. Auparavant, c'était dans le programme utilisateur.

Exemple 2

Considérez la classe CustomerProcessor ci-dessous:

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

Si je veux que processCustomer() soit indépendante 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 remplacez-le par quelque chose de plus générique, capable d'accepter différents types d'implémentations, de telle sorte que la processCustomers() fonctionnera simplement pour n'importe quelle implémentation fournie. Le code ci-dessus (instanciation de la classe requise SqlCustRepo par la logique du programme principal) est une méthode traditionnelle et il n'atteint pas cet objectif de découpler processCustomers() de l'implémentation de getAllCusts(). En inversion de contrôle, le conteneur instancie la classe d'implémentation requise (comme spécifié par, disons la configuration xml), l'injecte dans la logique du programme principal qui est lié selon les hooks spécifiés (par exemple par l'annotation @Autowired Ou la fonction getBean() méthode dans le cadre du printemps).

Voyons 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");
    }
}

On peut 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) a la responsabilité d'analyser le fichier xml, d'instancier le bean de type spécifique et de l'injecter dans le programme utilisateur. Le programme utilisateur n'a aucun contrôle sur la classe à instancier.

PS: l'IoC est un concept générique et est réalisé de plusieurs manières. Les exemples ci-dessus y parviennent par injection de dépendances.

Référence: article de Martin Fowler .

0
Maha