SOLID Partie V : Dependency Inversion Principle

Introduction

Le Dependency Inversion Principle (DIP) réside en deux règles:

  1. High level modules should not depend upon low level modules. Both should depend upon abstractions. ( Les modules de haut niveau ne doivent pas dépendre des modules de bas niveau. Les deux doivent dépendre d’abstractions.)
  2. Abstractions should not depend upon details. Details should depend upon abstractions ( Les abstractions ne doivent pas dépendre des détails. Les détails doivent dépendre des abstractions.)
Robert C. Martin

Le DIP est une sorte de corollaire des autres principes SOLID. En effet appliquer les autres principes conduit à faire reposer chaque classe sur des abstractions de telle manière que les deux règles soient respectées. De même chercher à appliquer le DIP conduit presque obligatoirement à obéir aux autres principes, en particulier l’Open/Closed Principle.

Appliquer le Dependency Inversion Principle

En vrac, quelques manières communes d’appliquer le DIP.

Strategy :
Dès qu’un objet doit en utiliser un autre, il est possible d’utiliser le design pattern Strategy et d’appliquer ainsi le DIP. Par exemple il est possible de convertir le code C++ suivant:

class Lamp {
  /*...*/
  public:
    void blink () {
      /*...*/
    }
}

class Button {
  private:
    Lamp * lamp;

  public:
    Button() {
      this->lamp = new Lamp();
    }

    void press () {
      lamp->blink();
    }
}

…en ce code, conforme au DIP, en rajoutant une interface que l’on va utiliser à la place de la classe concrète:

class Lamp {
  public:
    virtual void blink () = 0;
}

class ConcreteLamp : public Lamp {
  /*...*/
  virtual void blink () {
    /*...*/
  }
}

class Button {
  private:
    Lamp * lamp;

  public:
    Button(Lamp * lamp) {
      this->lamp = lamp;
    }

    void press () {
      lamp->blink();
    }
}

Inversion of Control
L’IoC consiste généralement à déléguer à un framework l’instanciation des objets concrets. Les objets ne manipulent alors plus que des interfaces et les objets concrets sont injectés soit dans le constructeur, soit par l’intermédiaire de setters (Dependency Injection).

Service Locator
Le design pattern Service Locator permet d’abstraire l’accès à un service, une librairie. Parfois proche dupattern Adapter

Adapter
Le design pattern Adapter permet de cacher les détails spécifiques d’une classe concrète derrière une interface adaptée à l’usage souhaité. L’idée de base de ce pattern est de créer une classe spéciale lorsque, par exemple, la récupération de certaines informations nécessite un grand nombre d’opérations complexes auprès d’un objet particulier. Afin d’éviter aux objets utilisant l’objet concerné de devoir connaître la logique de l’objet, on crée un Adapter qui s’en charge. Parfois il peut s’agit d’une simple fonction (wrapper function).

C’est d’autant plus intéressant d’utiliser le pattern Adapter lorsqu’il y a plusieurs classes a adapter de la même façon, des classes provenant de librairie différentes, afin de fournir une interface commune.

Exemple: Une application peut utiliser différents backends pour la persistance. Disons que l’on peut utiliser soit MySQL, soit PostgreSQL, soit MongoDB. Il serait impensable de créer des objets capables d’utiliser chacune des librairies correspondantes. On ne peut pas non plus raisonnablement tripler toutes les classes nécessitant l’accès à la couche de persistance. La solution est de créer une interface commune et un Adapter par librairie:

Plugin
Autre variation sur le thème, le concept de plugin. Un plugin est un élément tiers qui vient se greffer dans une application; Pour ce faire il faut généralement que le plugin implémente une certaine interface consacrée. L’application, quant à elle, ne manipule jamais concrètement les plugins mais se repose sur les interfaces qu’ils implémentent.

Conclusion

Le Dependency Inversion Principle est un principe assez vaste qui recouvre en partie les autres principes SOLID. En particulier, le DIP nous force à ne pas céder au naturel design par couche, où chaque couche utilise directement une couche de plus bas niveau. Au contraire, chacune des couches doit utiliser et implémenter des interfaces que les autres couches peuvent utiliser.

Pour le reste, si vous avez déjà lu les autres articles de notre série, vous devez déjà vous être imprégné de l’importance de l’abstraction, et le DIP ne devrait être qu’une formalité.

Pour aller plus loin

One thought on “SOLID Partie V : Dependency Inversion Principle

Leave a Reply