Qu'est-ce que le principe de l'inversion de dépendance et pourquoi est-il important?

Qu'est-ce que le principe de l'inversion de dépendance et pourquoi est-il important?

158
demandé sur Peter Mortensen 2008-09-15 16:53:20

13 réponses

Check this document: L'Inversion de la Dépendance Principe .

il dit en gros:

  • les modules de haut niveau ne devraient pas dépendre des modules de bas niveau. Les deux devraient dépendre d'abstractions.
  • Abstractions ne doit jamais dépendre de détails. Les détails devraient dépendre des abstractions.

quant à son importance, en bref: les changements sont risqués, et en dépendant d'un concept plutôt que d'une mise en œuvre, vous réduisez le besoin de changement aux sites d'appels.

effectivement, le DIP réduit l'accouplement entre différentes pièces de code. L'idée est que, bien qu'il existe de nombreuses façons de mettre en œuvre, par exemple, une exploitation forestière, la façon dont vous l'utiliseriez devrait être relativement stable dans le temps. Si vous pouvez extraire une interface qui représente le concept de journalisation, cette interface devrait être beaucoup plus stable dans le temps que son la mise en œuvre et les sites d'appel devraient être beaucoup moins affectés par les changements que vous pourriez apporter tout en maintenant ou en étendant ce mécanisme d'enregistrement.

en faisant dépendre l'implémentation d'une interface, vous avez la possibilité de choisir à l'exécution quelle implémentation est mieux adaptée à votre environnement particulier. Selon les cas, cela peut être intéressant aussi.

95
répondu Carl Seleborg 2017-01-03 21:17:47

Les livres de Développement Agile de Logiciels, Principes, Modèles et Pratiques Agile et Principes, Modèles et Pratiques en C# sont les meilleures ressources pour comprendre pleinement les objectifs et les motivations derrière le Principe d'Inversion des Dépendances. L'article "Le principe de L'Inversion de dépendance" est également une bonne ressource, mais en raison du fait qu'il s'agit d'une version condensée d'un projet qui a finalement fait son chemin dans les livres mentionnés précédemment, il laisse de côté quelques importants discussion sur le concept de propriété d'un paquet et d'une interface qui sont essentiels pour distinguer ce principe du conseil plus général de "programmer vers une interface, pas une implémentation" trouvé dans les Modèles de conception de livre (Gamma, et. Al.)

pour résumer, le principe de L'Inversion de dépendance concerne principalement inversant la direction conventionnelle des dépendances entre les composants de " niveau supérieur "et les composants de" niveau inférieur " de telle sorte que: "niveau inférieur" composants sont tributaires des interfaces propriété par le "niveau supérieur". (Note: la composante "niveau supérieur" se réfère ici à la composante nécessitant des dépendances/services externes, pas nécessairement sa position conceptuelle dans une architecture à plusieurs niveaux.) Ce faisant, le couplage n'est pas réduit autant qu'il est décalé de composants qui sont théoriquement moins précieux pour la réutilisation à des composants qui théoriquement plus précieux pour la réutilisation.

cela est obtenu en concevant des composants dont les dépendances externes sont exprimées en termes d'interface pour laquelle une implémentation doit être fournie par le consommateur du composant. En d'autres termes, les interfaces définies expriment ce dont le composant a besoin, et non la façon dont vous l'utilisez (par exemple "INeedSomething", pas "IDoSomething").

ce à quoi le principe de L'Inversion de dépendance ne fait pas référence est la pratique simple d'Abstraction des dépendances par l'utilisation d'interfaces (par exemple MyService → [ILogger ILogger ILogger]). Bien que cela sépare un composant du détail de mise en œuvre spécifique de la dépendance, cela n'inverse pas la relation entre le consommateur et la dépendance (par exemple [MyService → IMyServiceLogger] փ Logger.

L'importance du principe de L'Inversion de dépendance se manifeste principalement dans le développement de composants logiciels réutilisables qui reposent sur dépendances externes (journalisation, validation, etc.) car prendre des dépendances sur de telles dépendances demande à vos consommateurs d'exiger les mêmes dépendances. Cela peut poser problème lorsque les utilisateurs de votre bibliothèque choisissent d'utiliser une bibliothèque différente pour les mêmes besoins d'infrastructure (par exemple NLog vs. log4net), ou s'ils choisissent d'utiliser une version ultérieure de la bibliothèque requise qui n'est pas rétrocompatible avec la version requise par votre bibliothèque.

Un plus la discussion de ce principe en ce qui concerne l'utilisation simple des interfaces, L'Injection de dépendance, et le modèle D'Interface séparé peut être trouvé ici .

126
répondu Derek Greer 2013-01-08 17:29:06

quand nous concevons des applications logicielles, nous pouvons considérer les classes de bas niveau les classes qui mettent en œuvre des opérations de base et primaires (accès disque, protocoles réseau,...)...) et des classes de haut niveau les classes qui encapsulent logique complexe (flux d'affaires, ...).

les derniers s'appuient sur les classes de bas niveau. Une façon naturelle de mettre en œuvre de telles structures serait d'écrire des classes de bas niveau et une fois que nous les aurons pour écrire les classes de haut niveau complexes. Depuis les classes de haut niveau sont définies en termes d'autres cela semble la manière logique de le faire. Mais ce n'est pas une conception souple. Que se passe-t-il si nous devons remplacer une classe de bas niveau?

le principe de L'Inversion de dépendance stipule que:

  • les modules de haut niveau ne devraient pas dépendre des modules de bas niveau. Les deux devraient dépendre d'abstractions.
  • Abstractions ne devrait pas dépendre de détails. Les détails dépendent de abstraction.

ce principe vise à "inverser" la notion conventionnelle selon laquelle les modules de haut niveau dans les logiciels devraient dépendre des modules de niveau inférieur. Ici, les modules de haut niveau possèdent l'abstraction (par exemple, décider des méthodes de l'interface) qui sont mises en œuvre par les modules de bas niveau. Ainsi, les modules de niveau inférieur dépendent des modules de niveau supérieur.

10
répondu nikhil.singhal 2017-07-01 16:15:16

pour moi, le principe de L'Inversion de dépendance , tel que décrit dans article officiel , est vraiment une tentative malavisée d'augmenter la réutilisabilité des modules qui sont intrinsèquement moins réutilisables, ainsi qu'une façon de contourner un problème dans le langage C++.

le problème en C++ est que les fichiers d'en-tête contiennent généralement des déclarations de champs et de méthodes privés. Par conséquent, si un module C++ de haut niveau inclut le fichier d'en-tête pour un niveau bas module, il dépendra des détails réels mise en œuvre de ce module. Et qui, évidemment, n'est pas une bonne chose. Mais ce n'est pas un problème dans les langues plus modernes couramment utilisées aujourd'hui.

les modules de haut niveau sont intrinsèquement moins réutilisables que les modules de bas niveau parce que les premiers sont normalement plus spécifiques à l'application/au contexte que les seconds. Par exemple, un composant qui implémente un écran UI est du plus haut niveau et aussi très (complètement?) spécifique à l'application. Essayer de réutiliser un tel composant dans une autre application est contre-productif et ne peut conduire qu'à une ingénierie excessive.

ainsi, la création d'une abstraction séparée au même niveau d'un composant A qui dépend d'un composant B (qui ne dépend pas de A) ne peut être faite que si le composant A sera réellement utile pour la réutilisation dans des applications ou contextes différents. Si ce n'est pas le cas, alors appliquer DIP serait une mauvaise conception.

9
répondu Rogério 2009-08-30 22:30:49

en gros, ça dit:

La classe

devrait dépendre des abstractions (E. g interface, classes abstraites), pas de détails spécifiques (implémentations).

8
répondu martin.ra 2015-07-09 16:49:17
L'inversion de dépendance

bien appliquée donne flexibilité et stabilité au niveau de toute l'architecture de votre application. Cela permettra à votre application d'évoluer de façon plus sûre et plus stable.

architecture à couches traditionnelles

traditionnellement, une interface utilisateur à architecture en couches dépendait de la couche affaires et cela dépendait à son tour de la couche accès aux données.

http://xurxodev.com/content/images/2016/02/Traditional-Layered.png

vous devez comprendre la couche, le paquet ou la bibliothèque. Nous allons voir comment le code.

nous aurions une bibliothèque ou un paquet pour la couche d'accès aux données.

// DataAccessLayer.dll
public class ProductDAO {

}

et une autre logique commerciale de bibliothèque ou de paquet qui dépend de la couche d'accès aux données.

// BusinessLogicLayer.dll
using DataAccessLayer;
public class ProductBO { 
    private ProductDAO productDAO;
}

architecture à couches avec dépendance inversion

l'inversion de dépendance indique ce qui suit:

Les modules de haut niveau

ne devraient pas dépendre de modules de bas niveau. Les deux devraient dépendre d'abstractions.

Abstractions ne doit pas dépendre de détails. Les détails devraient dépendre des abstractions.

Quels sont les modules de haut niveau et de bas niveau? Pensée les modules tels que les bibliothèques ou les paquets, les modules de haut niveau seraient ceux qui ont traditionnellement des dépendances et de bas niveau dont ils dépendent.

En d'autres termes, le module de haut niveau où l'action est invoquée et de bas niveau où l'action est réalisée.

une conclusion raisonnable à tirer de ce principe est qu'il ne devrait pas y avoir de dépendance entre concrétions, mais qu'il doit y avoir une dépendance à l'égard d'une abstraction. Mais, selon l' approche que nous adoptons nous pouvons mal appliquer la dépendance liée à l'investissement, mais une abstraction.

Imaginez que nous adaptions notre code comme suit:

nous aurions une bibliothèque ou un paquet pour la couche d'accès aux données qui définit l'abstraction.

// DataAccessLayer.dll
public interface IProductDAO
public class ProductDAO : IProductDAO{

}

et une autre logique commerciale de bibliothèque ou de paquet qui dépend de la couche d'accès aux données.

// BusinessLogicLayer.dll
using DataAccessLayer;
public class ProductBO { 
    private IProductDAO productDAO;
}

bien que nous dépendions d'une abstraction la dépendance entre l'entreprise et l'accès aux données reste la même.

http://xurxodev.com/content/images/2016/02/Traditional-Layered.png

pour obtenir l'inversion de dépendance, l'interface de persistance doit être définie dans le module ou le paquet où se trouve cette logique ou ce domaine de haut niveau et non dans le module de bas niveau.

définir D'abord ce qu'est la couche domaine et l'abstraction de sa communication est défini persistance.

// Domain.dll
public interface IProductRepository;

using DataAccessLayer;
public class ProductBO { 
    private IProductRepository productRepository;
}

après la couche de persistance dépend du domaine, obtenir d'Inverser maintenant si une dépendance est définie.

// Persistence.dll
public class ProductDAO : IProductRepository{

}

http://xurxodev.com/content/images/2016/02/Dependency-Inversion-Layers.png

approfondir le principe

il est important de bien assimiler le concept, d'approfondir le but et les avantages. Si nous restons mécaniquement et apprendre le dépôt de cas typique, nous ne serons pas en mesure d'identifier où nous pouvons appliquer le principe de la dépendance.

mais pourquoi inverser une dépendance? Quel est l'objectif principal au-delà des exemples spécifiques?

tels communément permet aux choses les plus stables, qui ne dépendent pas de choses moins stables, de changer plus fréquemment.

il est plus facile de changer le type de persistance, base de données ou technologie permettant d'accéder à la même base de données que la logique du domaine ou les actions conçues pour communiquer avec la persistance. Pour cette raison, la dépendance est inversée parce qu'il est plus facile de changer la persistance si ce changement se produit. De cette façon, nous n'aurons pas à changer de domaine. La couche domaine est la plus stable de toutes, c'est pourquoi elle ne devrait pas dépendre de quoi que ce soit.

mais il n'y a pas que cet exemple de dépôt. Il existe de nombreux scénarios où cette le principe s'applique et il existe des architectures basées sur ce principe.

Architectures

il existe des architectures où l'inversion de dépendance est la clé de sa définition. Dans tous les domaines, c'est le plus important et il est abstractions qui indique le protocole de communication entre le domaine et le reste de l'emballage ou les bibliothèques sont définis.

Nettoyer "L'Architecture 1519830920"

Dans Architecture propre le domaine est situé au centre et si vous regardez dans la direction des flèches indiquant la dépendance, il est clair quelles sont les couches les plus importantes et stables. Les couches externes sont considérées comme des outils instables, donc évitez de dépendre d'eux.

Architecture Hexagonale

cela se passe de la même façon avec l'architecture hexagonale, où le domaine est également situé dans la partie centrale et les ports sont des abstractions de communication du domino vers l'extérieur. Ici encore, il est évident que le domaine est la plus stable et la dépendance traditionnelle est inversé.

8
répondu xurxodev 2017-07-01 16:13:26

une façon beaucoup plus claire d'énoncer le principe de L'Inversion de dépendance est:

vos modules qui encapsulent la logique commerciale complexe ne devraient pas dépendre directement d'autres modules qui encapsulent la logique commerciale. Ils devraient plutôt dépendre uniquement d'interfaces avec des données simples.

c'est-à-dire, au lieu de mettre en œuvre votre classe Logic comme les gens le font d'habitude:

class Dependency { ... }
class Logic {
    private Dependency dep;
    int doSomething() {
        // Business logic using dep here
    }
}

tu dois faire quelque chose comme:

class Dependency { ... }
interface Data { ... }
class DataFromDependency implements Data {
    private Dependency dep;
    ...
}
class Logic {
    int doSomething(Data data) {
        // compute something with data
    }
}

Data et DataFromDependency devraient vivre dans le même module que Logic , pas avec Dependency .

Pourquoi faire cela?

  1. les deux modules sont maintenant découplés. Lorsque Dependency change, vous n'avez pas besoin de changer Logic .
  2. comprendre ce que Logic fait est une tâche beaucoup plus simple: il fonctionne seulement sur ce qui ressemble à un ADT.
  3. Logic peut maintenant être testé plus facilement. Vous pouvez maintenant instancier directement Data avec de fausses données et le transmettre. Pas besoin de moqueries ou d'échafaudages de test complexes.
5
répondu mattvonb 2016-05-17 17:48:12

Inversion de contrôle (IoC) est un modèle où un objet reçoit sa dépendance par un cadre extérieur, plutôt que de demander un cadre pour sa dépendance.

Pseudocode exemple utilisant la recherche traditionnelle:

class Service {
    Database database;
    init() {
        database = FrameworkSingleton.getService("database");
    }
}

code similaire utilisant IoC:

class Service {
    Database database;
    init(database) {
        this.database = database;
    }
}

les avantages du CIO sont:

  • vous n'avez aucune dépendance sur un central cadre, donc cela peut être changé si voulu.
  • puisque les objets sont créés par injection, de préférence en utilisant interfaces, il est facile de créer une unité tests qui remplacent les dépendances par des versions simulées.
  • découplage du code.
5
répondu Staale 2017-06-22 07:39:15

de bonnes réponses et de bons exemples sont déjà donnés par d'autres ici.

La raison DIP est important car il assure le OO-principe "faiblement couplé design".

les objets de votre logiciel ne doivent pas entrer dans une hiérarchie où certains objets sont de niveau supérieur, dépendant d'objets de niveau inférieur. Les changements dans les objets de bas niveau se répercuteront alors sur vos objets de haut niveau ce qui rend le logiciel très fragile pour le changement.

vous voulez que vos objets 'top-level' soient très stables et pas fragiles pour le changement, donc vous devez inverser les dépendances.

5
répondu Hace 2017-06-22 07:40:39

Inversion de Contrôle des Conteneurs et l'Injection de Dépendance modèle par Martin Fowler est une bonne lecture. J'ai trouvé Head First Design Patterns un livre génial pour ma première incursion dans L'apprentissage DI et d'autres modèles.

1
répondu Chris Canal 2008-09-15 13:01:25

le point d'inversion de dépendance est de rendre le logiciel réutilisable.

l'idée est qu'au lieu de deux morceaux de code s'appuyant l'un sur l'autre, ils s'appuient sur une interface abstraite. Ensuite, vous pouvez réutiliser l'une ou l'autre sans l'autre.

la façon la plus courante d'y parvenir est par une inversion du conteneur de contrôle (IoC) comme le printemps en Java. Dans ce modèle, les propriétés des objets sont configurées par le biais d'une configuration XML au lieu de la les objets sortent et trouvent leur dépendance.

imaginez ce pseudo...

public class MyClass
{
  public Service myService = ServiceLocator.service;
}

MyClass dépend directement à la fois de la classe Service et de la classe ServiceLocator. Il a besoin des deux si vous voulez l'utiliser dans une autre application. Maintenant, imaginez ça...

public class MyClass
{
  public IService myService;
}

désormais, MyClass s'appuie sur une interface unique, l'interface IService. On laisserait le conteneur IoC fixer la valeur de cette variable.

donc maintenant, MyClass peut facilement être réutilisé dans d'autres projets, sans apporter la dépendance de ces deux autres classes avec elle.

encore mieux, vous n'avez pas à faire glisser les dépendances de MyService, et les dépendances de ces dépendances, et le... eh bien, vous obtenez l'idée.

1
répondu Marc Hughes 2017-06-22 07:37:35

je pense que j'ai un meilleur exemple (plus intuitif).

  • Imaginez un système (webapp) avec la gestion des employés et des contacts (deux écrans).
  • ils ne sont pas exactement liés donc vous les voulez chacun dans son propre module / dossier

ainsi, vous auriez un point d'entrée "principal "qui devrait connaître à la fois le module de gestion des employés et le module de gestion des contacts , et le devrait fournir des liens dans la navigation et accepter les requêtes api,etc. En d'autres termes, le module principal dépendrait de ces deux - il connaîtrait leurs contrôleurs, les routes et les liens qui doivent être rendus dans la navigation (partagée).

Node.exemple js

// main.js
import express from 'express'

// two modules, each having many exports
import { api as contactsApi, navigation as cNav } from './contacts/'
import { api as employeesApi, navigation as eNav } from './employees/'

const api = express()
const navigation = {
  ...cNav,
  ...eNav
}

api.use('contacts', contactsApi)
api.use('employees', employeesApi)

// do something with navigation, possibly do some other setup

de plus, veuillez noter il y a des cas (simples) où cela est totalement acceptable.


donc au fil du temps il va arriver à un point où il n'est pas si facile d'ajouter de nouveaux modules. Vous devez vous rappeler d'enregistrer l'api, la navigation, peut-être permissions , et ce main.js devient de plus en plus gros.

et c'est là qu'intervient l'inversion de dépendance. Au lieu que votre module principal soit dépendant de tous les autres modules, vous introduirez un "noyau" et ferez en sorte que chaque module s'enregistre lui-même.

Donc, dans ce cas, il s'agit d'avoir une idée de certains ApplicationModule, qui peut se soumettre à de nombreux services (routes, navigation, permissions) et le module principal peut rester simple (il suffit d'importer le module et de le laisser installer)

En d'autres termes, il s'agit de rendre architecture enfichable. Il s'agit de travail supplémentaire et de code que vous aurez à écrire/lire et maintenir de sorte que vous ne devriez pas le faire à l'avance, mais plutôt quand vous avez ce genre d'odeur.

ce qui est particulièrement intéressant est que vous pouvez faire n'importe quoi un plugin, même la couche de persistance - qui pourrait être utile si vous avez besoin de soutenir de nombreuses implémentations de persistance, mais ce n'est généralement pas le cas. Voir l'autre réponse pour l'image avec l'architecture hexagonale, c'est parfait pour l'illustration - il y a un noyau et tout le reste est essentiellement un plugin.

0
répondu Kamil Tomšík 2018-05-10 13:51:03

en plus d'autres réponses....

Permettez-moi d'abord de présenter un exemple..

qu'il y ait un hôtel qui demande un générateur de nourriture pour ses provisions. L'hôtel donne le nom de la nourriture (disons poulet) au générateur de nourriture et le Générateur renvoie la nourriture demandée à l'hôtel. Mais L'hôtel ne se soucie pas du type de nourriture qu'il reçoit et sert. Ainsi le générateur fournit les aliments avec une étiquette de "nourriture" à L'hôtel.

cette implémentation est en JAVA

FactoryClass avec une Méthode de Fabrique. Le Générateur De Nourriture

public class FoodGenerator {
    Food food;
    public Food getFood(String name){
        if(name.equals("fish")){
            food =  new Fish();
        }else if(name.equals("chicken")){
            food =  new Chicken();
        }else food = null;

        return food;
    }
}



Un Résumé/L'Interface De La Classe

public abstract class Food {

    //None of the child class will override this method to ensure quality...
    public void quality(){
        String fresh = "This is a fresh " + getName();
        String tasty = "This is a tasty " + getName();
        System.out.println(fresh);
        System.out.println(tasty);
    }
    public abstract String getName();
}



"1519120920 de Poulet" met en œuvre la Nourriture (Un Béton de Classe)

public class Chicken extends Food {
    /*All the food types are required to be fresh and tasty so
     * They won't be overriding the super class method "property()"*/

    public String getName(){
        return "Chicken";
    }
}



Poisson met en œuvre la Nourriture (Un Béton de Classe)

public class Fish extends Food {
    /*All the food types are required to be fresh and tasty so
     * They won't be overriding the super class method "property()"*/

    public String getName(){
        return "Fish";
    }
}



Enfin

L'Hôtel

public class Hotel {

    public static void main(String args[]){
        //Using a Factory class....
        FoodGenerator foodGenerator = new FoodGenerator();
        //A factory method to instantiate the foods...
        Food food = foodGenerator.getFood("chicken");
        food.quality();
    }
}

comme vous pouvez le voir L'hôtel ne sait pas si C'est un objet de poulet ou un objet de poisson. Il sait seulement que c'est un objet alimentaire I. L'Hôtel dépend de la Catégorie D'Aliments.

aussi, vous remarquerez que la classe de poisson et de poulet met en œuvre la classe alimentaire et n'est pas directement liée à L'hôtel. I. le poulet et le poisson dépendent également de la classe alimentaire.

, ce qui implique que la composante de haut niveau (hôtel) et la composante de bas niveau (poisson et poulet) dépendent toutes deux d'une abstraction (nourriture).

C'est ce qu'on appelle L'Inversion de dépendance.

0
répondu Revolver 2018-06-05 16:35:48