Nouvelles Chroniques d'Amethyste

Penser au Sens, pas au Verbe

Ecrire du code SOLID

4 Commentaires

Il est fréquent et il est normal de faire évoluer du code.

Vous avez certainement constaté que certains se prêtent mieux que d’autres à l’exercice. Ils sont plus malléables, plus souples, plus agiles. Alors pourquoi existe t’il du code qui ressemble à ceci lorsque l’on tente de le modifier:

2016-01-30_12-59-16

et d’autre plutôt à:

2016-01-30_13-02-06

 

Plusieurs années de recherche on conduit à dégager un petit nombre de caractéristiques qui assurent que du code restera flexible. Ces principes ont été compilés au début des années 2000 par Robert C. Martin sous l’acronyme de code SOLID. L’acronyme fut proposé par Michael Feather.

 

Je vais consacrer cet article à décortiquer ce qu’est du code SOLID. Comment on le met en œuvre, comment on le reconnaît. Puisque SOLID est en quelque sorte la grammaire qui sert à construire les patterns, je profiterai de cet article pour mettre en avant les patterns les plus courants, en tout cas le minimum que vous devez connaître.

SOLID est en effet la porte d’entrée pour comprendre le rôle des patterns, d’où sortent t’ils? Un pattern qui n’est pas SOLID n’est probablement pas un pattern.

 

Si vous souhaitez aller plus loin, je vous recommande chaudement l’acquisition de ce livre.

2016-01-30_13-07-10

C’est un livre agréable à lire, Martin est un développeur, mais très dense. Prévoyez plusieurs lectures.

C’est quoi du code SOLID?

SOLID est un acronyme qui désigne 5 principes. Je dis bien PRINCIPE, pas pattern. Si SOLID va nous dire ce qu’il faudrait faire, les patterns vont nous dire comment le faire.

  • S comme Single Responsability Principle (SRP)
  • O comme Open/Close Principle (OCP)
  • L comme Liskov Substitution Principle (LSP)
  • I comme Interface Segregation Principle (ISP)
  • D comme Dependency Inversion Principle (DIP)

Vous remarquez tout de suite que Dependency Inversion Principle a déjà fait l’objet d’un article. Je ne vais donc pas en parler longuement ici.

Il est également important de savoir qu’il n’existe pas de hiérarchie dans ces principes, même si je vois assez mal comment les appliquer à du code qui n’est pas au moins SRP.

La séquence permet juste d’avoir un acronyme mnémotechnique. Dans la pratique et on le constatera avec les démos, ces principes sont très imbriqués. C’est d’ailleurs assez normal car sur le fond ils disent la même chose: le code doit être facile à faire évoluer.

Cela n’a donc aucun sens d’entamer un chantier du style:

  1. Mardi on fait S
  2. Lundi O

 

Quelque chose apparaîtra également au cours de la suite: l’application des principes SOLID est très locale à une application. Un même code peut être SOLID dans une application et pas dans une autre, simplement parce que les axes de changement ne sont pas les mêmes.

Même si un audit n’est jamais inutile, à lui seul il ne suffira pas. On aura besoin d’autres sources d’information: l’avenir de ce code, comment va t’il évoluer. Cela suppose donc de disposer de son contexte, d’une compétence métier sur l’application. C’est un travail d’architecte.

Ne cherchez donc pas à monter des architectures stratosphériques dans laquelle pas une ligne de code n’est impliquée dans un pattern, tout est hyper flexible, les classes sont décorées d’autant d’interfaces que de membres….

L’effort n’en vaut pas la peine et ce genre de code fini même paradoxalement par ne plus être SOLID du tout, mais plutôt SOLIDE, c’est à dire rigide! C’est du code trop complexe, trop de classes hyper-spécialisées, on ne s’y retrouve plus.

Notre métier est un métier d’ingénieur. On ne nous demande pas de faire un truc très beau du point de vue de la théorie, mais de transformer l’expérience de labo en quelque chose d’utilisable dans la vie réelle.

Single Responsability Principle (SRP)

Une classe ne doit avoir qu’une seule raison de changer

Une raison de changer est ce que l’on appelle une responsabilité ou encore un axe de changement.

Ce principe est important. L’idée est que si une responsabilité évolue, alors on ne risque pas de casser les autres pour avoir introduit des couplages forts comme du code imbriqué l’un dans l’autre.

Deux auteurs peuvent être associés à ce principe: Tom DeMarco et Meilir Page-Jones.

 

Attaquons tout de suite avec une démo. Examinons la classe MessageStore que nous allons suivre dans plusieurs chapitres:

2016-01-30_15-20-35

Et plus particulièrement sa méthode Save. Combien de responsabilités voyez vous?

Trois sont faciles à découvrir:

2016-01-30_15-20-365

On trouve:

  1. Un service de log
  2. Un service d’écriture dans un fichier
  3. Un service de cache

Il en existe un 4ème plus subtil.

2016-01-30_15-25-33

Les services ne sont pas organisés dans Save au hasard. Ils sont activés dans un certain ordre. Ici le workflow est linéaire, mais il pourrait être plus complexe. La méthode Save a donc une fonction d’orchestration. Elle masque la complexité d’un algorithme au client qui la consomme. Il s’agit d’une FACADE. Un des patterns les plus courants.

La façade est une responsabilité à part entière. On a donc bien 4 responsabilités.

On constate de plus que la classe est auto-suffisante, c’est à dire qu’elle contient la totalité du code nécessaire au fonctionnement des services.

C’est ce problème que souligne SRP. La classe fait beaucoup de choses qui ne sont pas directement liées entre elles. Elle a trop de responsabilités et pourrait peu à peu tourner au code spaghetti dont la maintenabilité deviendra hors de portée, même des ninjas.

Certes cet exemple peut sembler caricaturale: le code est tout de même lisible, les responsabilités ne sont pas couplées entre elles. Mais ce n’est qu’un exemple, ce qui m’intéresse est l’idée et la structure du code.

 

La principale solution consiste à isoler les différents services dans des classes individuelles pour aboutir au schéma suivant:

2016-01-30_15-33-02

Comme prévu on dispose de 4 classes distinctes, une par responsabilité. Bien entendu les limites du découpages peuvent se discuter.

Vous pourriez par exemple décider que le service de cache est intrinsèque au service d’écriture, c’est à dire qu’il s’agit de sa plomberie interne et non pas d’une responsabilité à part entière. A mon avis c’est une erreur, mais vous voyez tout de même l’idée.

Je vous laisse à titre d’exercice découpler le code en écrivant les différentes classes et instancier les classes par exemple dans le constructeur.

Le code est encore perfectible, mais au moins il répond à SRP.

Les patterns typiques sont:

  • FACADE
  • Création de classes externes

Mais on en aura encore besoin d’autres pour que MessageStore soit vraiment SOLID.

Open/Close Principle (OCP)

Une entité doit être ouverte aux extensions et fermées aux modifications

Ce principe a été proposé par un français, Bertrand Meyer (l’inventeur du langage Eiffel) en 1988.

Au niveau du vocabulaire une entité peut désigner une fonction, un module, mais dans notre contexte ce sera plutôt une classe.

L’idée est qu’il doit être possible d’enrichir ou faire évoluer une classe, mais sans modifier le code existant sauf pour corriger un bug évidemment.

Le problème que ça pose est connu de tous les développeurs: ça marche, c’est testé, c’est utilisé dans des tas d’applis. Alors pourquoi prendre le risque de tout casser en modifiant un truc?

 

Oui, pourquoi?

………………………..

Quels sont les outils à notre disposition pour appliquer ce principe?

On appelle polymorphisme la capacité qu’à une classe de changer de comportement. Pour y parvenir on doit se servir d’abstractions, typiquement d’interfaces.

Il existe plusieurs stratégies possibles, mais avant d’en discuter examinons une situation:

2016-02-01_21-26-02

On reconnaît MessageStore telle qu’elle pourrait apparaître après la mise en œuvre de LSP. Nous allons nous intéresser au cas de Logger, la classe de log.

using Serilog;
 
namespace OCP.Domaine
{
   class Logger
   {
      public void Information(string message)
      { 
         Log.Information(message);
      }
   }
}

Ce n’est pas le body de Information() qui est remarquable, mais le Framework de log utilisé: Serilog.

Que se passe t’il si on préfère utiliser Log4Net ?

 

Une solution possible est évidemment de réécrire la classe avec Log4Net, mais on voit bien que l’on viole directement OCP. Ce n’est pas un problème dans une petite appli perso, cela le devient dans un cadre plus sophistiqué pour les raisons qui ont été évoquées.

Alors que faire?

 

——————————————————-

Ce que je voudrai, c’est écrire une classe qui utilise Log4Net, mais qui puisse se faire passer pour Logger afin de ne pas perturber le code déjà existant.

 

La première solution à laquelle on peut penser est l’héritage. Il s’agit d’ailleurs de la voie historique pour présenter OCP.

Cela ne pose pas de problème dans les langages qui acceptent l’héritage multiple et c’était plutôt la situation à la fin des années 80. Mais avec l’héritage simple on a un one-shot. Une fois l’héritage réalisé, on ne peut en avoir d’autres. Cela peut conduire à des rigidités dans le code ce qui est précisément ce que SOLID cherche à éviter.

De nos jours on préconise une autre méthode: la composition. On doit toujours essayer de privilégier la composition sur l’héritage.

La composition est un ensemble de techniques qui permettent de créer une classe en en agrégeant d’autres. De nombreux patterns (presque tous ceux du GoF) permettent de réaliser des compositions.

 

Je vais tout de même rester sur le terrain de l’héritage car le code sera moins verbeux et ne change pas la généralité de l’explication.

Je dois d’abord modifier Logger en rendant virtual son unique méthode:


public virtual void Information(string message)
{
   Log.Information(message);
}

 

Il nous reste plus qu’à implémenter une classe Log4NetLogger qui ressemblerai à celle-ci:


class Log4NetLogger: Logger
{
   public override void Information(string message)
   {
      // appel à Log4Net
   }
}

 

On a avancé, mais pas tout à fait terminé car pour l’instant MessageStore ne sait pas se servir de Log4NetLogger. On a pas le choix, il va falloir adapter MessageStore pour la rendre compatible avec OCP.

Logger agit comme une abstraction dans notre schéma. Il aurait sans douté été plus propre de créer une classe abstraite et de développer SeriloLogger, mais le principe est là.

Donc ce qu’il nous faut c’est une méthode qui sait instancier correctement une classe polymorphique. On appelle cela une FACTORY.

 

A vous de décider comment implémenter en pratique la factory et comment la placer dans le code, voici une solution possible. Nous modifions la propriété _log ainsi:


Logger _log;
protected virtual Logger Log
{
   get
   {
      if (_log == null)
      {
         _log = new Logger();
      }

      return _log;
   }
}

 

Et on oublie pas de nettoyer le constructeur.

Remarque: Par rapport à la classe de départ il aurait été plus logique d’implémenter de l’injection de dépendance auquel cas c’est le conteneur de dépendance qui joue le rôle de factory. On s’évite ainsi le travail de refactoring et on simplifie un peu le code.

 

Il ne reste plus qu’à prouver que le code obtenu répond bien à OCP. Il suffit de construire un exemple comme la classe Log4NetLogger:

 


class Log4NetMessageStore : MessageStore
{
   Logger _log;
   protected override Logger Log
   {
      get
      {
         if (_log == null)
         {
            _log = new Log4NetLogger();
      }

      return _log;
      }
   }
}

 

Voilà une solution. Peut-être pas la plus élégante du monde, mais elle met bien en avant les idées derrière OCP:

  • utilisation des abstractions
  • Faire évoluer sans modifier le code existant

 

Et n’oubliez pas: préférer la composition à l’héritage.

Liskov Substitution Principle

Une instance de type T doit pouvoir être remplacée par une instance de type G telle que G soit un sous type de T, sans que cela modifie la cohérence du programme

 

La formulation est tout de suite plus complexe. Si vous souhaitez vous amuser, vous pouvez aussi lire la publication originelle. Barbara Liskov et Jeannette Wing sont des théoriciennes, pas de doutes là dessus!!

La formulation est d’abord difficile et il faut bien admettre que le principe lui-même est complexe à appréhender car souvent on se rend compte d’une violation de LSP bien longtemps après que l’appli soit terminée.

 

Le principe s’intéresse aux conditions dans lesquelles une abstraction est compatible avec les objectifs recherchés par SOLID. C’est à dire permet t’elle de dériver des sous types dans de bonnes conditions.

Il ne s’agit pas du côté polymorphique, si on sous-type c’est en général parce que l’on veut modifier un comportement.

On s’intéresse plutôt à la cohérence que l’on peut aussi appelé correction du code.

Pour qu’une interface G réponde à LSP il doit être possible d’écrire des descendants T que l’on peut substituer à G dans le code sans voir apparaître des plantages ou de nouveaux prérequis du genre, « maintenant on doit lancer une méthode Initialize() avant d’utiliser T ».

C’est une contrainte plus forte qu’elle en à l’air car il n’est pas toujours possible de déceler le problème autrement qu’en écrivant effectivement un ou deux exemples de T. Beaucoup d’auteurs préconisent au moins 3.

Il n’est d’ailleurs pas forcément facile d’éviter ou rectifier une violation de LSP. L’exemple vient d’en haut. Regardez par exemple l’interface ICollection<T> du framework .Net:

2016-02-01_22-47-46

 

La collection expose diverses méthodes telles Add, Clear

ReadOnlyCollection est justement un de ses sous-types. Si on ne se sent pas en danger avec Contains, que faire de Add, Clear, Remove? Lever une exception? Ne rien faire?

Dans le premier cas on viole forcément LSP car tout code utilisant ICollection appelle forcément à un moment Add et plantera aussitôt avec ReadOnlyCollection.

Ne rien faire est t’il mieux? Tout dépend de ce qu’attend le code. Lorsque l’on lance Clear ou Add on attend tout de même que quelque chose se passe. Et encore on est dans le cas simple où le type de retour est void.

 

Que faire?

On pourrait imaginer que le code client de la classe teste explicitement le type de ICollection pour décider ou pas l’emploi des méthodes à problème. Est-ce possible sans changer le fond de l’appli? Ce n’est pas gagné, mais le remède est aussi pire que le mal. On viole directement SRP et OCR et puis rien ne dit que l’on dispose du source. Des tas d’autres cas peuvent très bien se présenter à l’avenir. C’est sans espoir.

 

On voit apparaître une caractéristique fréquente de LSP: la localité.

Le problème ne se pose que pour des applis qui attendent n’importe quelle instance de ICollection. Si dans votre contexte particulier vous savez que ReadOnlyCollection n’a pas de sens, aucun problème n’est à craindre. Souvenez vous, SOLID doit toujours s’interpréter dans le contexte des axes de changement de votre application.

 

De deux choses l’une: soit vous vivez avec le problème, soit une analyse dans le contexte de votre application permet de refactorer le code de façon à éliminer le problème.

 

Tout à l’heure nous verrons que ISP fournit des outils qui peuvent potentiellement nous aider. Soulignons pour l’instant une règle de conduite simple:

Si dans un cas extrême ‘interface n’avait qu’un seul membre, on aurait pas de problèmes avec LSP. Il suffirait à ReadOnlyCollection de ne pas implémenter les interfaces qui posent problème.

Ces interfaces qui contiennent beaucoup de membre sont typiquement créées avec l’outil de refactoring de VS à partir d’une classe concrète. On les appelle interface de tête.

Ce sont très souvent elles qui posent des problèmes avec LSP. Il est préférable de les éviter et c’est justement la problématique soulevée par le principe suivant.

 

Interface Segregation Principle

Un client ne doit pas être forcé de dépendre de méthodes qu’il n’utilise pas

C’est Robert Martin qui a le premier identifié ce principe.

Reprenons la discussion entamée dans le chapitre qui précède.

Historiquement les interfaces ont été développées pour introduire des couplages faibles. Ce n’est donc pas la classe concrète qui a besoin d’une interface, mais le client. On a donc aucune raison de proposer à un client des méthodes dont il n’a pas besoin.

Ce type d’interface qui cible le besoin est appelée interface de rôle. Elles sont par définition plus restreintes  que les interfaces de tête et surtout moins sensibles au problème LSP.

 

L’utilisation des interfaces de rôle résolvent également un problème qui nous aurait été utile tout à l’heure: comment faire pour supprimer des membres d’une entité?

En ajouter c’est facile et en général sans gros problème.

Avec les interfaces de rôle tout ce qu’il y a à faire est de ne pas implémenter les rôles (interfaces) dont on a pas besoin. Le code s’appuie uniquement sur le niveau d’abstraction dont il a besoin et rien de plus. On retrouve du même coup un lien avec OCP. Les principes SOLID fonctionnent tous ensembles.

 

Voyons les choses sur un exemple inspiré du bouquin de Martin.

Votre application propose de modéliser le fonctionnement d’une porte. Pour cela vous développez l’interface IDoor:


interface IDoor
{
   void Lock();
   void Unlock();
   bool IsDoorOpen { get; }
}

 

Ainsi qu’une implémentation très simple SimpleDoor:


class SimpleDoor : IDoor
{
   public bool IsDoorOpen
   {
      get
         {
            // prochainement du code plus intelligent
            return false;
         }
   }

   public void Lock()
   {
      // verrouille la porte
   }

   public void Unlock()
   {
      // déverrouille la porte
   }
}

 

Et pour finir une application cliente sans grande prétention:


public static void FaitDesTrucs(IDoor door)
{
   // Le client de IDoor
   // il ne dépend pas d'une implémentation particulière puisqu'il attend une instance de IDoor

   door.Lock();
   if (!door.IsDoorOpen)
   {
      Console.WriteLine("Attention! Impossible de verrouiller la porte");
   }
}

 

Le point important est de voir que la méthode attend une abstraction, pas une instance particulière de IDoor. Bien entendu on souhaite que IDoor réponde à tous les principes SOLID pour ne jamais avoir à modifier le client.

Cette technique de masquage d’une classe concrète derrière une abstraction s’appelle une STRATEGY. STRATEGY permet de fournir un couplage faible. Un pattern extrêmement simple comme on le voit.

Jusque là tout va bien, les affaires tournent et donc bien sûr les clients attendent du nouveau et en particulier un type de porte qui émet une alerte lorsque la porte est ouverte. L’alerte sera déclenchée par une méthode Timeout().

L’idée la plus simple est bien entendu d’enrichir IDoor de la méthode Timeout(). Pour gérer le déclenchement de l’alerte, votre équipe R&D a développé la classe Timer suivante:


class Timer
{
   IDoor _client;

   /// <summary>
   ///
   /// </summary>
   /// <param name="timeout">Durée au bout de laquelle une alerte est levée</param>
   /// <param name="client"></param>
   public void Register(TimeSpan timeout, IDoor client)
   {
      _client = client;
      // attend sur la durée timeout
      _client.Timeout();
   }
}

 

La classe expose une méthode qui permet à une porte, plus exactement une instance de IDoor, de s’enregistre auprès d’un séquenceur très rudimentaire. Celui-ci attend une durée transmise en paramètre (je n’ai pas implémenté cette partie) et appelle la méthode IDoor.Timeout() afin de lever une alerte: son, email, coup de canon…

 

Il n’y a plus qu’à développer le nouveau modèle de porte, TimedDoor:

 


class TimedDoor : IDoor
{
   Timer timer = new Timer();

   public void Lock()
   {
      // verrouille la porte

      timer.Register(new TimeSpan(0, 5, 0), this);
}

   public void Timeout()
   {
      // déclenche une alerte
   }

   // reste de la classe

}

 

Jusque là tout va bien, notre nouvelle porte fonctionne sans problème. Toutefois un petit problème apparaît:

2016-02-02_09-24-01

 

La porte d’origine ne fonctionne plus très bien. Ce qui est le pire, c’est à cause d’une fonctionnalité qui ne la concerne pas. Alors on pourrait certes compléter SimpleDoor avec un Timeout() vide. Mais de modifications en modifications le code au départ très simple va devenir très compliqué. Au bout d’un moment on ne saura plus vraiment ce que l’on a vraiment besoin d’implémenter pour créer une nouvelle porte, sans compter que l’on augmente à chaque fois la sensibilité à LSP.

 

Si on analyse les choses on constate que le problème vient du fait que IDoor et Timeout() sont deux responsabilités différentes: toutes les portes ont besoin de IDoor, mais seules quelques portes ont besoin de Timeout().

Deux responsabilités, donc en application de SRP il est préférable de créer deux interfaces:


interface ITimerClient
{
   /// <summary>
   /// Méthode à appeler pour lever une alerte
   /// </summary>
   void Timeout();
}

 

On laisse IDoor comme elle était à l’origine et on modifie TimedDoor:


class TimedDoor : IDoor, ITimerClient

 

Il ne reste plus qu’à remplacer IDoor par ITimerClient dans la classe Timer qui n’a pas besoin de savoir ouvrir ou refermer une porte.

 

Une technique très simple, basée sur l’usage d’interfaces de rôles, pour pourvoir ajouter et supprimer des responsabilités dans une classe.

L’idée importante que promeut ISP est de ne faire dépendre le code que de ce dont il a besoin, pas moins, pas plus. C’est ce que nous avons fait avec Timer et TimedDoor.

 

La méthode proposée est certainement la plus simple, mais elle n’est pas la seule. Une alternative possible est l’utilisation d’un ADAPTATEUR. C’est d’ailleurs la méthode proposée par Martin dans son livre. J’ai développé le pattern adaptateur dans un article précédent en parallèle d’un pattern voisin: PROXY. Ce pattern fournit par ailleurs une solution intéressante pour le cas où l’on ne peut accéder au source de TimedDoor, mais que l’on souhaite tout de même ajouter une interface.

 

Dependency Inversion Principle

Il faut dépendre des abstractions, pas des détails (implémentation)

Je vais être assez bref car j’ai déjà rédigé un article complet sur ce principe. Retenez surtout que ce n’est pas la même chose que l’injection de dépendance qui lui est un pattern, pas un principe.

Je me conterai de faire remarquer que la démo précédente et d’ailleurs les autres aussi fonctionnent, c’est précisément une illustration de ce principe.

DIP est le principe qui justifie STRATEGY et la plupart des patterns.

Publicités

4 réflexions sur “Ecrire du code SOLID

  1. Félicitation et Excellent comme article, quelques légers raccourcis dans les explications, qui pourraient perdre certaines personnes pas à l’aise avec ces pratiques (surtout pour la partie LSP). Par contre je me permets pour votre définition du « D »
    « Dependency Injection Principle », tout le monde se trompe mais c’est vraiment la « Dependency inversion principle ». L’inversion et non l’Injection et donc cela inverse aussi votre explication en effet Injection de Dépendance est un Pattern que l’autre est donc un Principe. L’injection de dépendances est un mécanisme qui permet d’implémenter le principe de l’inversion de contrôle d’ailleurs votre article (encore une fois excellent) le traite de cette manière.

    Il y aura surement un rajout à votre article sur l’inversion de dépendance 😉

    Je vous laisse quelques liens :
    http://butunclebob.com/ArticleS.UncleBob.PrinciplesOfOod
    https://drive.google.com/file/d/0BwhCYaYDn8EgMjdlMWIzNGUtZTQ0NC00ZjQ5LTkwYzQtZjRhMDRlNTQ3ZGMz/view
    https://en.wikipedia.org/wiki/SOLID_(object-oriented_design)
    https://en.wikipedia.org/wiki/Dependency_inversion_principle
    https://en.wikipedia.org/wiki/Dependency_injection

    Encore une fois vraiment top. D’ailleurs tout votre site.
    Si cela vous intéresse on peut partager sur Design et Principe un de mes « dadas » entre autre dans le Dev.

    Merci encore pour ce blog et belle continuation.
    Franck.

    • Vous avez tout à fait raison, c’est Dependency Inversion Principle!!!!
      J’ai corrigé, dire que j’ai tout relu 10 fois!

      N’hésitez pas à reprendre tout ou partie de cet article et même à poster des correctifs.
      LSP est je pense un des principes les plus subtils en tout cas celui sur lequel j’ai le plus ramé.

      Vos liens sont intéressants, surtout le premier que je ne connaissais pas.

      • Désolé de « ré insister » 😉 mais alors votre article sur lequel vous faites référence dans votre partie D ne convient pas tout à fait car c’est un article sur l’Injection de Dépendance (IoC et Service Locator).
        C’est un des patterns (ou façon) que l’on peut utiliser pour faire de l’Inversion de Dépendance, mais vous avez aussi tous les Patterns dit de Création qui peuvent faire l’affaire, dont le Factory Pattern en autre.
        (Les 23 GOF étant séparés en 3 familles)

        Sachant qu’ici on parle d’Inversion de Dépendance c’est de découpler les Modules dont les deux principes sont :

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

        Je pense que de faire une petite présentation du principe lui-même simple permet de comprendre ensuite comment d’autres Patterns peuvent accomplir cela dont l’injection de Dépendance que l’on appelle d’ailleurs ( Dependency Injection framework) et le mot framework a son sens comme vous le signaler dans votre article justement.

        Je vous laisse quelques liens de nouveau, voyez si cela a du sens et merci encore de ce merveilleux travail.

        Oncle BOB DIP Pdf.
        https://drive.google.com/a/cleancoder.com/file/d/0BwhCYaYDn8EgMjdlMWIzNGUtZTQ0NC00ZjQ5LTkwYzQtZjRhMDRlNTQ3ZGMz/view?pref=2&pli=1

        Celui-ci est vraiment bon car ensuite il donne aussi l’explication du pourquoi la liaison (DIP) et Injection de Dépendance.
        http://aspiringcraftsman.com/2008/12/28/examining-dependency-inversion/

        Encore une fois super et a très bientôt.
        Franck.

  2. Ok, je vois ce que vous voulez dire
    Effectivement si dans l’article j’expliquais la différence entre injection de dépendance et inversion de dépendance, je me suis ensuite focalisé sur un cas particulier. Cet article devait surtout servir d’intro aux deux suivants sur Unity et Autofac.

    Du coup c’est effectivement un peu rapide de me contenter de renvoyer à cet article.

Laisser un commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l'aide de votre compte WordPress.com. Déconnexion / Changer )

Image Twitter

Vous commentez à l'aide de votre compte Twitter. Déconnexion / Changer )

Photo Facebook

Vous commentez à l'aide de votre compte Facebook. Déconnexion / Changer )

Photo Google+

Vous commentez à l'aide de votre compte Google+. Déconnexion / Changer )

Connexion à %s