Injection de dépendances #05 : Injection de dépendances à l’aide du projet Castle#

Dans cet article on va utiliser Castle Windsor 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 le projet Castle Windsor.

Comme tous les frameworks implémentant l’injection de dépendances, Castle Windsor met à disposition un container (ou Kernel) permettant de résoudre les dépendances. Il faut bien entendu signifier à ce dernier les dépendances avant de lui demander de les résoudre.

Côté implémentation c’est dans la même idée que Ninject, sauf qu’on peut déclarer les dépendances soit par code soit dans le fichier de configuration .config

On va commencer par ajouter les références aux librairies Castle nécessaires pour faire de l’injection de dépendances dans le projet console à savoir : Castle.Core.dll, Castle.MicroKernel.dll et enfin Castle.Windsor.dll.

Pour pouvoir charger ces dll, il suffit de rajouter à votre app.config les lignes suivantes :

 

<?xml version="1.0" encoding="utf-8" ?>
<
configuration>

...
        <
configSections>
                <
section name="castle" type="Castle.Windsor.Configuration.AppDomain.CastleSectionHandler, Castle.Windsor" />
        </
configSections>

...

</configuration>

 

Si vous n’avez pas de fichier .config dans votre projet, vous pouvez le rajouter en faisant click droit sur le projet -> Add -> New Item… et choisir l’élément « Application Configuration File »

Ensuite on va déclarer les dépendances au container de Castle, pour cet article j’ai choisi de la faire dans le fichier de config. Les deux types de classes qu’on va injecter pour notre exemple sont la classe RizAuCurry et la classe Poelle. Pour ce faire il faut rajouter ces quelques lignes dans le fichier de config :

 

<?xml version="1.0" encoding="utf-8" ?>
<
configuration>

...
        <
castle>
                <
components>

                        <
component id="Cuisinier"
                                           type="DemoLibrary.Implementation.Cuisinier, DemoLibrary">

                        </
component>
                        <
component id="Ustensile"
                                           service="DemoLibrary.Interfaces.IUstensile, DemoLibrary"
                                           type="DemoLibrary.Implementation.Poelle, DemoLibrary">

                        </
component>
                        <
component id="Plat"
                                           service="DemoLibrary.Interfaces.IPlat, DemoLibrary"
                                           type="DemoLibrary.Implementation.RizAuCurry, DemoLibrary">

                        </
component>

                </
components>
        </
castle>

...

</
configuration>

 

La balise Component nous permet de déclarer une dépendance. L’attribut id correspond au nom qui sera utilisé pour la résolution de dépendance, l’attribut service désigne l’interface à résoudre et est composé de 2 parties : le namespace + nom de l’interface et le nom de la dll. Enfin l’attribut type qui permet de spécifier le type de l’objet qui sera retourné par le kernel lors de la résolution de dépendance et qui est aussi composée de 2 parties : le namespace + nom de l’interface et le nom de la dll.

Maintenant il ne reste plus qu’à écrire le code principal dans notre projet console et au lieu d’instancier directement les classes on va demander au container Caste de nous renvoyer une instance du type dont on a besoin. Et ce qui est intéressant avec Castle c’est qu’on bénéficie de la résolution récursive de dépendances et qu’on n’a pas besoin de demander au container de résoudre toutes les dépendances une par une mais en faisant l’instanciation, à chaque fois qu’il va rencontrer une dépendance il va voir dans son fichier de config s’il peut la résoudre ce qui réduit considérablement le code à écrire.

    class Program
    {
       
static void Main(string[] args)
        {
           
//Instancier le container castle
            IWindsorContainer container =
           
new WindsorContainer(
               
new XmlInterpreter(new ConfigResource("castle")));

           
//Demande de r?solution des d?pendances de Cuisinier 
            //et de retourner une instance
            Cuisinier cuisinier = container.Resolve<Cuisinier>();

           
Console.WriteLine(cuisinier.Cuisiner());

           
Console.Read();
        }
    }

 

Voici le résultat :

 

clip_image002

Avantages :

- Le fait d’utiliser le fichier de config pour la déclaration de dépendances présente un avantage certain qui est de pouvoir changer le type retourné par le container sans avoir besoin de recompiler tout le code ;

- Simple à mettre en œuvre ;

- Le container est générique ;

- Résolution récursive de dépendances.

Inconvénients

- La déclaration de dépendances au sein du fichier de config peut être une source d’erreurs lors de l’exécution puisque ce n’est pas du compilé.

Conclusion :

L’implémentation de l’injection de dépendances au sein du framework Castle Windsor est très complète, souple et puissante. La prise en main est simple et le code à écrire se trouve réduit. La seule question qui reste ouverte c’est la question de la performance.

c#3 | Castle Project | DI | IOC
Friday, December 19, 2008 1:51:59 PM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

 

Injection de dépendances #04 : Injection de dépendances à l'aide de Spring.NET#

Dans cet article on va utiliser Spring.NET 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 Spring.NET.

Le framework Spring.NET est basé sur la version java du framework Spring. Il permet de faciliter et d'accélérer le développement d'applications .NET en proposant des implémentations de différentes fonctionnalités comme la programmation orientée aspect (AOP), l'accès aux données avec NHibernate ou encore l'injection de dépendances.

Comme tous les frameworks implémentant l’injection de dépendances, Spring.NET met à disposition un container permettant de résoudre les dépendances. Il faut bien entendu signifier à ce dernier les dépendances avant de lui demander de les résoudre.

Côté implémentation c’est totalement différent des implémentations des autres frameworks. La déclaration des dépendances se fait dans le fichier de configuration .config

On va commencer par ajouter la référence à Spring.Core.dll dans le projet console.

Pour pouvoir charger cette dll, il suffit de rajouter à votre app.config les lignes suivantes :

 

<?xml version="1.0" encoding="utf-8" ?>
<
configuration>

...
        <
configSections>
                <
sectionGroup name="spring">
                        <
section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core" />
                        <
section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" />
                </
sectionGroup>
        </
configSections>

...
</
configuration>

 

Si vous n’avez pas de fichier .config dans votre projet, vous pouvez le rajouter en faisant click droit sur le projet -> Add -> New Item… et choisir l’élément « Application Configuration File »

Ensuite on va déclarer les dépendances au container de Spring. En fait en ce qui concerne Spring on définit des alias sur les types qu’on veut injecter. Les deux types de classes qu’on va injecter pour notre exemple sont la classe RizAuCurry et la classe Poelle. Pour ce faire il faut rajouter ces quelques lignes suivantes dans le fichier de config :

 

<?xml version="1.0" encoding="utf-8" ?>
<
configuration>

...


        <
spring>
                <
context>
                        <
resource uri="config://spring/objects" />
                </
context>
                <
objects xmlns="http://www.springframework.net">
                        <
object name="Plat" type="DemoLibrary.Implementation.RizAuCurry, DemoLibrary" singleton="false" />
                        <
object name="Ustensile" type="DemoLibrary.Implementation.Poelle, DemoLibrary" singleton="false" />
                </
objects>
        </
spring>

...

</
configuration>

 

La balise Object nous permet de définir un alias vers un type. L’attribut name correspond à l’alias, l’attribut type est composé de 2 parties : le namespace + nom de la classe et le nom de la dll. Enfin l’attribut singleton permet de spécifier si c’est un singleton.

Ensuite, on va créer un « wrapper » sur le container de Spring, qui va juste nous renvoyer le container lorsqu’on en a besoin. On pourrait bien évidemment se passer de cette étape mais un peu d’organisation ça ne fait pas de mal.

using Spring.Context;
using Spring.Context.Support;

    public static class Context
    {
       
private static IApplicationContext applicationContext = null;

       
static Context()
        {
            applicationContext =
ContextRegistry.GetContext();
        }

       
public static IApplicationContext ApplicationContext
        {
           
get { return applicationContext; }
        }

    }

 

Maintenant il ne reste plus qu’à écrire le code principal dans notre projet console et au lieu d’instancier directement les classes on va demander au container Spring de nous renvoyer une instance du type dont on a besoin.

    class Program
    {
       
static void Main(string[] args)
        {
           
//Le container Spring renvoye un objet qui
            //correspond ? l'alias Plat
            IPlat plat = (IPlat)Context.ApplicationContext["Plat"];

           
//Le container Spring renvoye un objet qui
            //correspond ? l'alias Ustensile
            IUstensile ustensile = (IUstensile)Context.ApplicationContext["Ustensile"];

           
//Instaciation de la classe Cuisinier
            Cuisinier chef = new Cuisinier(ustensile,plat);
            
           
Console.WriteLine(chef.Cuisiner());

           
Console.Read();
        }
    }
 

 

Voici le résultat :

image

Avantages

L’implémentation de l’injection de dépendances avec le framework Spring.NET offre beaucoup d’avantages :

- Le fait d’utiliser le fichier de config pour la déclaration des alias présente un avantage certain qui est de pouvoir changer la classe vers laquelle pointe un alias sans avoir besoin de recompiler tout le code.

Inconvénients

- Pas de résolution récursive de dépendances, on doit explicitement demander au container les objets correspondant aux alias dont on a besoin.

- Le container n’est pas générique ce qui nous oblige à faire un cast à chaque fois que ce dernier nous renvoie un objet.

- La déclaration des alias au sein du fichier de config peut être une source d’erreurs lors de l’exécution puisque ce n’est pas du compilé.

Conclusion :

Spring.NET est un bon framework d’injection de dépendances grâce à son côté souple concrétisé par l’utilisation du fichier de config mais je dois avouer que le fait de ne pas avoir la résolution récursive de dépendances est un inconvénient non négligeable et qui est à mon avis éliminatoire au sein d’un grand projet.

c#3 | DI | IOC | Spring
Thursday, December 18, 2008 11:44:47 PM (Romance Standard Time, UTC+01:00) #    Comments [2]  | 

 

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