• 4 heures
  • Facile

Ce cours est visible gratuitement en ligne.

course.header.alt.is_certifying

J'ai tout compris !

Mis à jour le 15/02/2023

"D" pour Dependency Inversion Principle ou principe d'inversion des dépendances

Principe d'inversion des dépendances

Nous voici arrivés au dernier des principes SOLID, le principe d'inversion des dépendances (DIP).

Voyons la définition qu'en donne Robert C. Martin dans son ouvrage Agile Software Development. Elle se compose de deux parties :

1. Les modules de haut niveau ne doivent pas dépendre des modules de bas niveau. Les deux doivent dépendre d'abstractions.

2. Les abstractions ne doivent pas dépendre des détails. Les détails doivent dépendre des abstractions.

Je sais que cela paraît complexe, mais si vous avez appliqué les principes ouvert/fermé et de substitution de Liskov à votre code, sachez que vous respectez déjà ce nouveau principe.

Rappelez-vous, le principe ouvert/fermé voudrait que votre application soit ouverte à l'extension et fermée à la modification. Pour y parvenir, vous avez eu recours à des interfaces proposant diverses implémentations.

Avec le principe de substitution de Liskov, vous avez appris que vous devriez pouvoir remplacer des implémentations par d'autres sans bugger votre application.

L'objectif est d'éviter la création d'un système fortement couplé dans lequel chaque module fait directement référence à des modules de niveau inférieur. C'est pour cette raison que vous devez opter pour l'abstraction afin d'obtenir un système moins couplé.

Exemple 1 : couplage fort

Créons un exemple simple, une application de notification présentant un couplage fort.

public class Email
{
    public string AdresseDestinataire { get; set; }
    public string Objet { get; set; }
    public string Contenu { get; set; }

    public void EnvoyerEmail()
    {
      //Envoi de l'e-mail
    }
}

public class SMS
{
    public string Contenu { get; set; }
    public string Message { get; set; }

    public void EnvoyerSMS()
    {
      //Envoi du SMS
    }
}

public class Notification
{
    private Email _email;
    private SMS _sms;

public Notification()
    {
      _email = new Email();
      _sms = new SMS();
    }

   public void Envoyer()
    {
      _email.EnvoyerEmail();
      _sms.EnvoyerSMS();
    }
}

Regardez de plus près la classe  Notification  , une classe de niveau supérieur. Elle dépend à la fois de la classe  Email  et de la classe  SMS  , deux classes de niveau inférieur. On appelle cette structure une implémentation concrète. En réalité, il faudrait créer une abstraction de cette implémentation.

Votre objectif ? Découpler ces classes et réduire les dépendances.

Comment vérifier rapidement votre code pour déterminer quelles parties sont associées à un couplage fort ?

Une astuce à ne pas oublier : regardez le mot-clé new. En général, plus votre code compte d'instances du mot-clé new, plus son couplage est fort.

Pourquoi ?

Tout simplement parce que vous instanciez des objets, ce qui génère des dépendances.

Exemple 2 : refactorisation permettant de respecter le principe d'inversion des dépendances

Refactorisons cet exemple  Notification  pour éliminer ces dépendances.

Tout d'abord, introduisez une abstraction fiable et que d'autres peuvent implémenter.

public interface IMessage
{
    void EnvoyerMessage();
}

public class Email : IMessage
{
    public string AdresseDestinataire { get; set; }
    public string Objet { get; set; }
    public string Contenu { get; set; }

    public void EnvoyerMessage()
    {
      //Envoi de l'e-mail
    }
}

public class SMS : IMessage
{
    public string NumeroTelephone { get; set; }
    public string Message { get; set; }

    public void EnvoyerMessage()
    {
      //Envoi du sms
    }
}

public class Notification
{
    private ICollection<IMessage> _messages;

    public Notification(ICollection<IMessage> messages)
    {
      this._messages = messages;
    }

    public void Envoyer()
    {
      foreach(var message in _messages)
        {
            message.EnvoyerMessage();
        }
    }
}

Analysons nos modifications.

  1. Nous avons procédé à l'abstraction de  EnvoyerMessage()  dans une interface appelée  IMessage  .

  2. Les classes  Email  et  SMS  implémentent l'interface  IMessage  .

  3. La classe  Notification  dépend désormais de l'abstraction  IMessage  et non plus de l'implémentation concrète d'une classe.

Ces modifications permettent à la classe  Notification  de s'intéresser uniquement à l'existence d'une abstraction capable d'envoyer des messages.

Voilà, vous avez compris le principe d'inversion des dépendances.

Testez par vous-même !

https://api.next.tech/api/v1/publishable_key/2A9CAA3419124E3E8C3F5AFCE5306292?content_id=3fb0250a-edb8-48df-a487-a4df2e756647&first_name={{first_name}}&last_name={{last_name}}&email={{email}}&user_id={{email}}&security_check={{date_created}}&provider=thinkific_mm 

En résumé 

  • Le principe d'inversion des dépendances se définit comme suit :

    • Les modules de haut niveau ne doivent pas dépendre des modules de bas niveau. Les deux doivent dépendre des abstractions.

    • Les abstractions ne doivent pas dépendre des détails. Les détails doivent dépendre des abstractions.

  • Si vous respectez les principes ouvert/fermé et de substitution de Liskov, vos classes respecteront aussi le principe d'inversion des dépendances.

  • Prêtez attention au nombre de fois où vous utilisez le mot-clé new dans votre application. Cela peut être un indicateur d'un couplage trop fort.

  • L'objectif est de limiter les implémentations concrètes en favorisant les abstractions. 

Maintenant que vous savez ce qu'il faut faire, voyons ensemble ce qu'il ne faut pas faire. Découvrez les principes STUPID pour savoir comment les éviter !

Exemple de certificat de réussite
Exemple de certificat de réussite