Injection de dépendances #03 : Injection de dépendances en utilisant Ninject#

Dans cet article on va utiliser NInject pour faire de l’injection de dépendances au sein de l’exemple précédemment établit dans le premier article et modifié dans le second . Pour cela on va tout d’abord télécharger et installer NInject.

Ensuite on va ouvrir notre solution de l’exemple, et on va ajouter une référence dans le projet console vers la librairie Ninject.Core.dll.

clip_image002

NInject.Core est la librairie qui va nous permettre d’implémenter l’injection de dépendance.

Tout d’abord on doit commencer par déclarer les dépendances c'est-à-dire que telle interface correspond à telle implémentation, ceci se fait dans un « Module ».

Pour ce faire, on va tout simplement implémenter un module personnalisé et surcharger sa méthode load de la classe StandardModule fournie par NInject de la manière suivante :

    public class CustomModule : StandardModule
    {
       
public override void Load()
        {
           
//D?claration des d?pendances
            Bind<IPlat>().To<PattesBolognaises>();
            Bind<
IUstensile>().To<Marmitte>();
        }
    }

 

On dit dans la méthode load ci dessus que l’implémentation de l’interface IPlat est la classe PattesBlognaises, Idem pour IUstensile et Marmitte.

Pour résoudre les dépendances, il faut tout simplement instancier le container IKernel en lui passant le module qui va bien.

    class Program
    {
       
static void Main(string[] args)
        {
           
//Instanciation du module
            CustomModule module = new CustomModule();
           
//Instanciation du kernel charg? de r?soudre les d?pendances
            IKernel kernel = new StandardKernel(module);
           
//Demande de r?solution des d?pendances de Cuisinier 
            //et de retourner une instance
            Cuisinier p = kernel.Get<Cuisinier>();
           
Console.WriteLine(p.Cuisiner());

            Console.Read();
        }
    }

 

En faisant kernel.Get<Cuisinier>() on va demander au container de résoudre les dépendances de la classe Cuisinier et de nous en retourner une instance. On remarque au passage que la méthode Get est générique.

Voici ce qu’on retrouve à l’execution :

clip_image002[4]

Pour switcher d’une implémentation à une autre, on a 2 choix :

- 1er cas : On dit que c’est exclusif, c'est-à-dire qu’une interface correspond à une et une seule implémentation pour une instance de l’application, on va alors mettre des « case » dans le module pour choisir les implémentations à lier aux interfaces par rapport à un critère bien définit.

- 2ème cas : On dit que pour une instance de l’application une interface peut être liée à plusieurs implémentations. Dans ce cas, on est alors amené à implémenter plusieurs modules.

Le 1er cas étant déjà implémenté ci-dessus, je vais plutôt m’intéresser à implémenter le 2ème cas. Pour ce faire, je vais construire un nouveau module appelé RizModule qui va lier l’interface IPlat à l’implémentation RizAuCurry et l’interface IUstensile à l’implémentation Poelle comme suit :

    public class RizModule : StandardModule
    {
       
public override void Load()
        {
           
//D?claration des d?pendances
            Bind<IPlat>().To<RizAuCurry>();
            Bind<
IUstensile>().To<Poelle>();
        }
    }

 

Voici le programme main et son résultat :

    class Program
    {
       
static void Main(string[] args)
        {
           
//Instanciation du module
            CustomModule module = new CustomModule();
           
//Instanciation du kernel charg? de r?soudre les d?pendances
            IKernel kernel = new StandardKernel(module);
           
//Demande de r?solution des d?pendances de Cuisinier 
            //et de retourner une instance
            Cuisinier p = kernel.Get<Cuisinier>();
           
Console.WriteLine(p.Cuisiner());

           
//Instanciation du module
            RizModule rizModule = new RizModule();
           
//Instanciation du kernel charg? de r?soudre les d?pendances
            IKernel rizKernel = new StandardKernel(rizModule);
           
//Demande de r?solution des d?pendances de Cuisinier 
            //et de retourner une instance
            Cuisinier chefRiz = rizKernel.Get<Cuisinier>();
           
Console.WriteLine(chefRiz.Cuisiner());

           
Console.Read();
        }
    }

 

clip_image002[6]

Conclusion :

NInject est très simple à apprivoiser.

Le fait de pouvoir faire la liaison entre l’implémentation et l’interface par code à l’aide du « Bind » présente un l’avantage d’être du compilé et donc c’est moins générateur d’erreur qu’un fichier xml de configuration par exemple où on peut faire des fautes de frappe par exemple. En contre partie on ne dispose pas de la souplesse d’un fichier xml parce qu’à chaque fois qu’on aura envie de changer le Binding d’une interface à une implémentation on est obligé de recompiler le code, alors qu’avec un fichier xml il suffit de le mettre à jour.

Le container de NInject est générique, on récupère directement une instance de la classe souhaitée sans avoir besoin de faire un cast.

Le kernel résout récursivement les dépendances, c'est-à-dire qu’il résout les dépendances de l’objet mais aussi les dépendances des dépendances de l’objet … Ce qui nous facilite grandement le travail.

c#3 | DI | IOC | Ninject
Wednesday, November 26, 2008 12:34:54 PM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

 

Injection de dépendances #02 : Injection de dépendances à la main#

Cet article se base sur l'exemple énoncé dans l'article d' introduction sur l'injection de dépendances.

La première chose à faire pour pouvoir implémenter une injection de dépendance est d’enlever ces dépendances. Pour ce faire on va remplacer toutes les instanciations / occurrences des implémentations par l’utilisation des interfaces. On va alors créer des interfaces pour les classes et faire que ces dernières les implémentent. On pourra alors être complètement découplé des implémentations des autres classes puisqu’on n’utilisera que les interfaces dans le code de la classe. Enfin on va injecter les dépendances à partir de notre classe principale.

Pour notre exemple on va avoir une interface pour les plats et une interface pour les ustensiles.

    public interface IUstensile
    {
       
String Utiliser();
    }

 

    public interface IPlat
    {
       
String GetInformation();
    }

 

Les classes RizAucurry et Poelle vont implémenter respectivement les interfaces IPlat et IUstensile.

    //Implémente l'interface IPlat
    public class RizAuCurry : IPlat
    {
       
public String GetInformation()
        {
           
return ("du riz au curry");
        }
    }

 

    //Implémente l'interface IUstensile
    public class Poelle : IUstensile
    {
       
public String Utiliser()
        {
           
return ("à la Poelle");
        }
    }

 

En ce qui concerne la classe Cuisinier on ne va plus utiliser que les interfaces et on va passer en paramètre à son constructeur les dépendances le moment venu.

 

    public class Cuisinier
    {   //La classe Cuisinier n'utilise que des interfaces
        //et non les implémentations
        IPlat _plat;
       
IUstensile _ustensile;

       
public Cuisinier(IPlat plat, IUstensile ustensile)
        {
            _plat = plat;
            _ustensile = ustensile;
        }
       
public String Cuisiner()
        {
           
return "Je cuisine " + _plat.GetInformation() + " " + _ustensile.Utiliser();
        }
    }

 

La classe Main du projet console va ensuite créer tous les objets nécessaires et injecter les dépendances à l’instance de la Cuisinier comme suit :

    class Program
    {
       
static void Main(string[] args)
        {   
           
//Création des dépendances
            RizAuCurry platRiz = new RizAuCurry();
           
Poelle ustensilePoelle = new Poelle();
           
//Injectoin des dépendances
            Cuisinier chef = new Cuisinier(platRiz, ustensilePoelle);
           
Console.WriteLine(chef.Cuisiner());

           
Console.Read();
        }
    }

 

Et maintenant si on veut que le cuisinier cuisine autre chose que le Riz au curry à la poêle il suffit de construire les bonnes classes, que ses dernières implémentent les interfaces IPlat et IUstensile et c’est tout. Si on construit la classe PattesBolognaises qui implémente IPlat et la classe Marmitte qui implémente IUstensile on peut faire quelque chose comme ça :

    class Program
    {
       
static void Main(string[] args)
        {   
           
//Création des dépendances
            RizAuCurry platRiz = new RizAuCurry();
           
Poelle ustensilePoelle = new Poelle();
           
//Injectoin des dépendances
            Cuisinier ancienChef = new Cuisinier(platRiz, ustensilePoelle);
           
Console.WriteLine(ancienChef.Cuisiner());

           
PattesBolognaises platPattes = new PattesBolognaises();
           
Marmitte ustensileMarmitte = new Marmitte();
           
Cuisinier nouveauChef = new Cuisinier(platPattes, ustensileMarmitte);
           
Console.WriteLine(nouveauChef.Cuisiner());
           
Console.Read();
        }
    }

Voici le résultat :

clip_image002

 

Et voila comment on peut faire de l’injection de dépendance à la main.

- Remplacer les instanciations (dépendances) par l’utilisation des interfaces pour découpler les classes les unes des autres.

- Créer et injecter les dépendances quand on en a besoin

Il existe des frameworks qui mettent à notre disposition des Container et qui nous permettent d’implémenter l’injection de dépendance. Ces containers nous permettent de :

- Résoudre les dépendances après avoir déclaré la correspondance des interfaces avec leurs implémentations respectives.

- Sortir le code d’injection de dépendances de la logique de l’application

- Résolution récursive des dépendances. (Pour certains frameworks)

Parmis ces frameworks je vais m’intéresser particulièrement dans mes prochains articles à NInject, Castle Windsor et Spring.net.

c#3 | DI | IOC
Tuesday, November 25, 2008 4:19:57 PM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

 

L’injection de dépendances (Dependency Injection) #01 : Introduction#

On n’est jamais à l’abri d’un changement dans une application déjà finalisée voir déployée depuis un bon bout de temps. Bien qu’aujourd’hui la plupart des applications sont écrites de façon à ce que toutes ses couches soient complètement indépendantes en suivant le pattern MVC ou autre, il faut reconnaître que bien souvent les classes à l’intérieur d’une même couche sont toujours très dépendantes ce qui nous vaut un travail conséquent pour changer une règle métier par exemple.

C’est là où l’injection de dépendances intervient, il s’agit de construire des classes complètement découplées des implémentations des classes en utilisant les interfaces. Ensuite, on injecte la dépendance (on résout la dépendance) au moment ou on a besoin d’une instance de la classe, on dit tout simplement que telle interface correspond à telle implémentation. On peut injecter la dépendance par constructeur, par setter, … Le but est de créer des classes plus faciles à changer et indépendantes de l’implémentation des autres classes pour qu’on puisse changer l’implémentation correspondant aux interfaces très simplement et d’une façon dynamique.

L’injection de dépendances peut se faire à la main, mais il existe aussi des frameworks qui nous permettent de l’implémenter comme NIINJECT, Spring.NET ou encore Castle Windsor.

Prenons cet exemple :

Un cuisinier utilise un ustensile pour cuisiner un plat.

clip_image001

    public class Poelle
    {
       
public String Utiliser()
        {
           
return ("? la Poelle");
        }
    }

 

    public class RizAuCurry
    {
       
public String GetInformation()
        {
           
return ("du riz au curry");
        }
    }

 

    public class Cuisinier
    {
       
RizAuCurry plat;
       
Poelle ustensile;

       
public Cuisinier()
        {
            plat =
new RizAuCurry();
            ustensile =
new Poelle();
        }
       
public String Cuisiner()
        {
           
return "Je cuisine " + plat.GetInformation() + " " + ustensile.Utiliser();
        }
    }

 

Les deux classes RizAuCurry et Poelle se contentent de renvoyer leurs noms et la méthode Cuisiner de la classe Cuisinier ne fait qu’afficher quel plat prépare le cuisinier et à l’aide de quel ustensile.

Faisons maintenant une application console qui va juste instancier une classe Cuisinier et appeler la méthode Cuisiner.

    class Program
    {
       
static void Main(string[] args)
        {
           
Cuisinier chef = new Cuisinier();
           
Console.WriteLine(chef.Cuisiner());
           
Console.Read();

        }

    }

 

Voici le résultat :

clip_image002

Ce qu’on peut remarquer tout de suite en regardant ces classes c’est que la classe Cuisinier est directement dépendante des deux classes RizAuCurry et Poelle. Et qu’est ce qui se passe si on voulait faire faire autre chose au cuisinier ? parce que le Riz au curry c’est bon mais à la longue je pense que ça pourrais devenir un peu lourd.

c#3 | DI | IOC
Tuesday, November 25, 2008 1:04:29 PM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

 

All content © 2012, Zied Nemili