Les 3 types de Dependency Injection

Récemment j’ai écrit un article autour du concept de « Dependency Injection et Inversion of Control « avec une concentration sur l’utilisation d’un Container IoC.

Cette fois-ci, je veux donner un aperçu  des 3 différents possibilités, qu’on peut utiliser pour adapter le patron de conception Dependency Injection dans son codage :

  1. Constructor Injection
  2. Setter Injection
  3. Interface Injection

Constructor Injection

Passer une dépendance à la classe dépendant en utilisant le Constructor

namespace TypesDi
{
    class Program
    {
        static void Main(string[] args)
        {
            var coach = new Coach();

            var driver = new Driver(coach);
        }
    }

    public class Driver
    {
        private readonly IDriveable vehicle;

        public Driver(IDriveable vehicle)
        {
            this.vehicle = vehicle;
        }
    }

    public interface IDriveable {}

    public class Coach : IDriveable {}
}

Setter Injection

  • Créer un setter dans la classe dépendant
  • Utilisez un setter pour mettre la dépendance en place
namespace TypesDi
{
    class Program
    {
        static void Main(string[] args)
        {
            var coach = new Coach();

            var driver = new Driver();

            driver.MyVehicle = coach;

        }
    }

    public class Driver
    {
        public IDriveable MyVehicle { get; set; }
    }

    public interface IDriveable {}

    public class Coach : IDriveable {}
}

Interface Injection

  • La classe dépendante mets en œuvre un interface
  • L’injector utilise l’interface pour mettre la dépendance en place
namespace TypesDi
{
    class Program
    {
        static void Main(string[] args)
        {
            var coach = new Coach();

            var driver = new Driver();

            ((IDriveable)driver).Inject(coach);
        }
    }

    public class Driver : IDriveable
    {
        private IDriveable vehicle;

        public void Inject(IDriveable vehicle)
        {
            this.vehicle = vehicle;
        }
    }

    public interface IDriveable
    {
        void Inject(IDriveable vehicle);
    }

    public class Coach : IDriveable
    {
        public void Inject(IDriveable vehicle)
        {
            throw new NotImplementedException();
        }
    }
}

 

Bref, IoC et DI en C# (…en utilisant Windsor Castle, pour la reine ;)

Avez-vous déjà écouté l’expression « Inversion of Control (IoC) « ou « Dependency Injection (DI) « mais vous ne savez pas exactement de quoi il s’agît ou pourquoi vous pourriez en avoir besoin ? Si c’est le cas, veuillez continuer à lire chère lectrice ou cher lecteur.

Tout d’abord, il y a une grande différence entre Dependency Injection et Inversion of Control. DI c’est un patron de conception, tandis que IoC c’est plutôt un mécanisme, ou un framework utilisé pour injecter automatiquement des dépendances. Castle Windsor, c’est un exemple pour un framework IoC.

Ben alors, et c’est quoi exactement Dependency Injection maintenant ? Laissez-moi vous donner une explication simple :

dependency injection

Disons, la reine du royaume du Commonwealth visite le Canada. Pour son tour du pays, elle a besoin d’un conducteur. La reine dit à son conducteur : « Allez, on y conduit de Montréal à Toronto «.

Bien sûr, le conducteur aura besoin d’un véhicule et va alors aller en chercher un, soit un 4×4 ou soit un Rolls Royce. De toute façon, la reine n’a aucune influence sur le choix du véhicule. Et ça pourrait être un problème, surtout si le conducteur va prendre un vieux coche au lieu du Rolls Royce.

La reine et le conducteur seront insatisfaites, lui parce qu’il doit choisir, et elle à cause de son choix. C’est à dire, il y aura une relation indésirable entre le conducteur et le véhicule. Donc ça serait beaucoup mieux, si la reine pourrait donner le véhicule désiré au conducteur et disait : « Allez, Go là-bas ! «  …Non ? 😉

En tout cas, le framework IoC s’occupe de toutes les dépendances qui pourraient apparaitre dans une telle relation.

D’accord, faisons-nous une clarification du exemple au-dessus avec un codage en C#, d’abord le patron de conception Dependency Injection :

namespace DiLaReine
{
    class Program
    {
        static void Main(string[] args)
        {
        }
    }

    public class Reine
    {
        public void GiveOrder()
        {
            var charles = new Conducteur();

            var rollsRoyce = new RollsRoyce();
            var coach = new HorseBuggy();

            charles.ExecuteOrder(rollsRoyce);
            charles.ExecuteOrder(coach);
        }
    }

    public class Conducteur
    {
        public void ExecuteOrder(IDriveable vehicule)
        {
            vehicule.Drive();
        }
    }

    public interface IDriveable
    {
        void Drive();
    }

    public class RollsRoyce : IDriveable
    {
        public void Drive()
        {
        }
    }

    public class HorseBuggy : IDriveable
    {
        public void Drive()
        {
        }
    }
}

En gros…
…résumons-nous : DI c’est rien d’autre que contrôler ou résoudre des dépendances d’une mise en œuvre, en travaillant avec des interfaces par exemple. Comme ça, on peut donner une dépendance à un composant ou une classe appelé pendant la période d’action.

Et pourquoi donc tout cela ? SOLID, surtout S et O => Sinlge Responsibility Principle et Open-Closed Principle. DI/IoC fait ça donc beaucoup plus facile de maintenir les dépendances entre les objets dans une application.

Par exemple si on crée un appli pour des platforms différentes, on peut abstraire le codage commun et ajouter des spécificités d’un platform pendant la période d’action.

CASTLE WINDSOR

castle windsor

Dans notre exemple si-dessus, on pourrait dire, que Castle Windsor c’est le garage, qui s’occupe du parc de véhicules. Mais avant qu’on continue, il faut comprendre qu’il y a une différence entre Inversion of Control et Inversion of Control Container.

Inversion of Control Container

Le principe en software design IoC, est un principe où un codage custom d’un logiciel peut recevoir des instructions en regardant l’état de répétition du logiciel. Les frameworks IoC, comme par exemple Windsor, Unity, Ninject ou Spring utilisent ce principe pour maintenir les objets d’un logiciel.

Cela c’est le travail d’un Inversion of Control Container, en bref. C’est-à-dire, un IoC Container utilise le principe IoC pour maintenir les classes d’un logiciel et leur cycle vital : la création,  la destruction, la configuration et leurs dépendances. Ça a l’avantage que les classes n’ont pas besoin d’obtenir ou configure les classes dont elles dépendent. Avantage : La flexibilité et le soulagement de la réutilisation et testabilité des classes d’un logicel

Exemple en utilisant Windsor

J’utilise un appli console simple. Dans le projet, il faut ajouter un Nuget package : Castle Windsor.

windsor castle

Après il faut créer les classes, interfaces et le IoC containter avec castle windsor.

using System;
using Castle.MicroKernel.Registration;
using Castle.Windsor;
using Castle.Windsor.Installer;

namespace WindsorEtLaReine
{
    class Program
    {
        static void Main(string[] args)
        {
            IWindsorContainer cont = new WindsorContainer();
            cont.Install(FromAssembly.This());
            cont.Register(Component.For<Vehicle>());
            //cont.Register(Component.For<IDriveable>().ImplementedBy<RollsRoyce>());
            cont.Register(Component.For<IDriveable>().ImplementedBy<HorseBuggy>());

            var conducteur = cont.Resolve<Vehicle>();
            conducteur.StartEngine();

            Console.ReadKey();
        }
    }

    public class Vehicle
    {
        private IDriveable o1;

        public Vehicle(IDriveable d1)
        {
            o1 = d1;
        }

        public void StartEngine()
        {
            o1.Drive();
        }
    }

    public interface IDriveable
    {
        void Drive();
    }

    public class RollsRoyce : IDriveable
    {
        public void Drive()
        {
            Console.WriteLine("Rolls Royce drive");
        }
    }

    public class HorseBuggy : IDriveable
    {
        public void Drive()
        {
            Console.WriteLine("Attention, coach on the road...");
        }
    }
}

Cet exemple montre la base d’utilisation d’un IoC container et le patron de conception Dependency Injection. J’espère que l’exemple leve un peu le voile sur le grand sujet Dependency Injection et Inversion of Control.

Events en C#

Les Events sont des actions comme key press, clic ou mouvement de souris, etc. ou des notifications créés par un système. En général, les applications doivent correspondre aux events quand ils occurrent, par exemple interrompre un logiciel. Principalement, les events sont utilisées pour la communication entre les procès.

Delegates et Events

La classe qui contient un évènement est utilisée pour publier un event. Cette classe s’appelle la classe publisher. Quelque autre classe qui accepte cet évènement est la calsse subscriber. Les events utilisent donc le model publisher-subscriber :
Un publisher, c’est un objet qui contient la définition d’un évènement et le delegate. L’objet d’une classe publisher invoque un event.
Un subscriber, c’est un objet qui accept un event et fourni un event handler. Le delegate dans la classe publisher invoque la méthode (event handler) d’une classe subscriber.

Voici un exemple :

namespace EventsExample
{
    class Program
    {
        static void Main(string[] args)
        {
            var button = new Button();
            button._clicked = A;
            button._clicked += B;
            button._clicked += C;
        }

        private static void C(object sender, EventArgs args)
        {
            throw new NotImplementedException();
        }

        private static void B(object sender, EventArgs args)
        {
            throw new NotImplementedException();
        }

        private static void A(object sender, EventArgs args)
        {
            var button = sender as Button;
            Console.WriteLine(button.Name);
        }
    }

    class ClickArgs : EventArgs
    {
        public int X { get; set; }
        public int Y { get; set; }

        public DateTime ClickTime { get; set; }

        public string Name { get; set; }
    }

    delegate void MyClickDelegate(object sender, ClickArgs args);

    class Button
    {
        public string Name { get; set; }      

        //public MyClickDelegate _clicked;

        public EventHandler<ClickArgs> _clicked;

        public void Click()
        {
            var args = new ClickArgs
            {
                X = 1,
                Y = 2,
                ClickTime = DateTime.Now
            };
            _clicked(this, args);
        }
    }
}

SOLID

SOLID, un acronyme qui représente les 5 principes de base pour la programmation orientée objet. SOLID permet le développement de logiciel plus fiable et plus robuste.

S: Single Responsability Principle
Une classe doit avoir une seule responsabilité. Cela réduit le risque d’effectuer des autres comportements sans relation.

O: Open/Closed Principle
Une classe doit être ouverte à l’extension, mais fermée à la modification. C’est-à-dire, ça doit être possible de faire une extension d’un comportement en créant une nouvelle classe – mais sans changement d’un codage existent.

L: Liskov Substitution Principle
Une instance de type T doit pouvoir être remplacée par une instance de type G, tel que G sous-type de T, sans que cela ne modifie la cohérence du programme

I: Interface Segragation Principle
Préférer plusieurs interfaces spécifiques pour chaque client plutôt qu’une seule interface générale.

D: Dependency Inversion Principle
Il faut dépendre des abstractions, pas des implémentations.
C’est-à-dire, SOLID aide le programmeur à créer des meilleurs logiciels. L’expression a été crée par Michael Feathers and Bob Martin.

Observer patron de conception (observer pattern)

Le patron de conception observer fait partie des patrons du groupe << Behavioral >> de GoF, qui s’occupe de la communication parmi les objets différents, on dirait. Il y a beaucoup de façons différents d’implémention d’un observer pattern. Le plus connu est probablement la gestion d’événements (Event Handling).

Le nom décrit plus ou moins la fonctionnalité : Observer. Donc un observer c’est quelque chose que regarde des autres objets. Donc l’observer est basée sur le Hollywood Principle : << Don’t call us, we call you >>.

Dans le codage suivant on jette un coup d’œil sur un exemple facile en C# :

namespace ObserverPattern
{
    #region interfaces

    interface ISubject
    {
        void Subscribe(Observer o);
        void Unsubscribe(Observer o);
        void Notify();
    }

    interface IObserver
    {
        void Update(string a);
    }

    #endregion

    #region classes

    /// <summary>
    /// Observer: maintient une référence au Subject et le statut actuel
    /// </summary>
    /// <seealso cref="ObserverPattern.IObserver" />
    class Observer : IObserver
    {
        public void Update(string animal)
        {
            Console.WriteLine("Nouveau au zoo");
            Console.WriteLine(animal);
            Console.WriteLine();
        }
    }

    /// <summary>
    /// Subject: maintient une liste d'Observers
    /// </summary>
    /// <seealso cref="ObserverPattern.ISubject" />
    class Subject : ISubject
    {
        private List<Observer> observers = new List<Observer>();
        private List<String> zoo = new List<String>(); 

        public void Subscribe(Observer o)
        {
            observers.Add(o);
        }

        public void Unsubscribe(Observer o)
        {
            observers.Remove(o);
        }

        public void Notify()
        {
            foreach (var o in observers)
            {
                o.Update(zoo[zoo.Count - 1]);
            }
        }

        public void ReleaseAnimal(string animal)
        {
            zoo.Add(animal);
            ArchiveChanged();
        }

        private void ArchiveChanged()
        {
            Notify();
        }
    }

    #endregion

    class Program
    {
        static void Main(string[] args)
        {
            var o = new Observer();

            var animal = new Subject();

            animal.Subscribe(o);

            animal.ReleaseAnimal("Caribou");

            animal.ReleaseAnimal("Ours");

            Console.ReadKey();
        }
    }
}

Façade patron de conception (facade design pattern)

Le patron de conception Façade (facade design pattern) définit une interface supérieure, qui facilite l’utilisation d’un sous-système. C’est-à-dire, la Façade offre une interface unifiée à un set des interfaces.

facade

Voici l’idée des acteurs principales :

  • Façade (Voiture application)
  • Classes d’un sous-système (Moteur, Pneus, carosserie, …)
namespace Facade
{
    /// <summary>
    /// SubClassOne
    /// </summary>
    class SubClassOne
    {
        public void MethodSubClassOne()
        {
            Console.WriteLine("Called SubClassOne");
        }
    }
    /// <summary>
    /// SubClassTwo
    /// </summary>
    class SubClassTwo
    {
        public void MethodSubClassTwo()
        {
            Console.WriteLine("Called SubClassTwo");
        }
    }
    /// <summary>
    /// Facade
    /// </summary>
    class Facade
    {
        private SubClassOne _one;
        private SubClassTwo _two;

        public Facade()
        {
            _one = new SubClassOne();
            _two = new SubClassTwo();
        }

        public void MethodeFacade()
        {
            _one.MethodSubClassOne();
            _two.MethodSubClassTwo();
        }
    }

    /// <summary>
    /// Main Program: Startup
    /// </summary>
    class Program
    {
        static void Main(string[] args)
        {
            var facade = new Facade();

            facade.MethodeFacade();

            Console.ReadKey();
        }
    }
}

Adapteur patron de conception (adapter pattern)

Le patron de conception Adapter est un des design patterns de GoF dans la catégorie des patrons de conception << Structural >>. C’est-à-dire, il s’agît d’un patron qui s’occupe de la composition classe objet.

Le but d’un Adapteur, c’est de convertir l’interface d’une classe en une autre interface qui est demandé par le client. L’adapteur fait des classes incompatible travailler ensemble, donc des classes qui ne peuvent pas travailler ensemble normalement.

adapter

Les acteurs principals dans ce pattern sont les suivants :

  • Target
  • Adapter
  • Adaptee
  • Client

Voici un exemple simplifié pour montrer la fonctionnalité :

namespace Adapter
{
    /// <summary>
    /// Target: defines the domain-specific interface that Client uses.
    /// </summary>
    class Target
    {
        public virtual void Request()
        {
            Console.WriteLine("Called Target");
        }
    }

    /// <summary>
    /// Adapter: adapts the interface Adaptee to the Target interface.
    /// </summary>
    /// <seealso cref="Adapter.Target" />
    class Adapter : Target
    {
        private Adaptee _adaptee = new Adaptee();

        public override void Request()
        {
            _adaptee.OtherRequest();
        }
    }

    /// <summary>
    /// Adaptee: defines an existing interface that needs adapting.
    /// </summary>
    class Adaptee
    {
        public void OtherRequest()
        {
            Console.WriteLine("Called Other");
        }
    }

    class Program
    {
        /// <summary>
        /// Client: collaborates with objects conforming to the Target interface.
        /// Mains the specified arguments.
        /// </summary>
        /// <param name="args">The arguments.</param>
        static void Main(string[] args)
        {
            var target = new Adapter();
            target.Request();

            Console.ReadKey();
        }
    }
}

 

Factory patron de conception (Factory Design Pattern)

Le patron de conception Factory, l’un de GoF, a la fonctionnalité de créer  des objets. L’avantage du Factory c’est qu’on implémente une abstraction qu’isole la logique pour déterminer quelle classe sera utilisée pour créer un objet. C’est important que le patron de conception Factory utilise un interface ou une classe classe base.

factory

Voici un exemple :

namespace Factory
{
    /// <summary>
    /// Interface Animal
    /// </summary>
    interface IAnimal
    {
        string Name { get; }
    }

    /// <summary>
    /// Caribou Class
    /// </summary>
    /// <seealso cref="Factory.IAnimal" />
    class Caribou : IAnimal
    {
        public string Name
        {
            get { return "Caribou"; }
        }
    }

    /// <summary>
    /// Ours class
    /// </summary>
    /// <seealso cref="Factory.IAnimal" />
    class Ours : IAnimal
    {
        public string Name
        {
            get { return "Ours"; }
        }
    }

    /// <summary>
    /// Animal Factory
    /// </summary>
    class AnimalFactory
    {
        public static IAnimal CreateAnimal(int num)
        {
            IAnimal AnimalSelector = null;

            switch (num)
            {
                case 1:
                    AnimalSelector = new Caribou();
                    break;
                case 2:
                    AnimalSelector = new Ours();
                    break;
                default:
                    AnimalSelector = new Caribou();
                    break;
            }

            return AnimalSelector;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            for (int i = 0; i < 3; i++)
            {
                var animal = AnimalFactory.CreateAnimal(i);
                Console.WriteLine("Id = {0}, position = {1} ", i, animal.Name);
            }

            Console.ReadKey();
        }
    }
}

Singleton patron de conception (design pattern) en C#

Le patron de conception Singleton, assure qu’une classe ne pêut que être instancié uniquement une fois et que la classe mets en place un point d’accées global.

singleton

La classe << Singleton >> défini une opération qui laisse l’accés à l’instance au client. En plus, elle est responsable pour la création et la maintenance de cette instance unique.

On utilise le singleton en général, quand

  1. on n’a qu’une seule instance et
  2. il faut gérer cette instance

En regardant l’anatomie d’une classe singleton, normalement le constructor est modifié private et l’accès public static.

Voici un exemple pour une classe Singleton, static initialization :

namespace Singleton
{
    /// <summary>
    /// Singleton class
    /// </summary>
    internal sealed class Singleton
    {
       private static Singleton _instance = null;
       

       private Singleton()
       {
       }

       public static Singleton Instantiate()
       {
          if (_instance == null)
       {
          _instance = new Singleton();
       }
          return _instance;
    }
 }

    /// <summary>
    /// Main program
    /// </summary>
    class Program
    {
        static void Main(string[] args)
        {
            Singleton single1 = Singleton.Instanciate();
            Singleton single2 = Singleton.Instanciate();

            if (single1 == single2)
            {
                Console.WriteLine("single1 and single2 are the same instance");
            }

            Console.ReadKey();
        }
    }
}

Le problème dans cette solution << Singleton >> est que la classe singleton peut introduire des dependencies pas nécessaire et qu’elle peut être utilisé par plusieurs threads en même temps, donc elle pourrais créer plusierus instances. Par exemple dans un application Web, si il y a plusieurs HTTP requests. Un peu plus secoure c’est la façon lazy en utilisant un << thread locker >>. Dans ce cas là il faut modifier la classe sealed et référencer l’instance par le constructor.

Voici un exemple pour une classe singleton, lazy implementation .NET :

namespace Singleton
{
    /// <summary>
    /// Singleton class
    /// </summary>
    internal sealed class Singleton
    {
       private static Singleton _instance = null;
       private static readonly object _locker = new object();

    private Singleton()
    {
    }

    public static Singleton Instantiate
    {
       get
       {
          lock (_locker)
       {
          if (_instance == null)
          {
             _instance = new Singleton();
          }
       }
       return _instance;
     }
   }
 }

    /// <summary>
    /// Main program
    /// </summary>
    class Program
    {
        static void Main(string[] args)
        {
            Singleton single1 = Singleton.Instantiate;
            Singleton single2 = Singleton.Instantiate;

            if (single1 == single2)
            {
                Console.WriteLine("single1 and single2 are the same instance");
            }

            Console.ReadKey();
        }
    }
}

Un bon exemple pour l’utilisation d’un singleton pourrais être un log file ou une classe pour configurer l’accés à un base de données. En plus on ne crée pas beaucoup des variables dans un namespace global.

Néanmoins il y a quelques désavantages, qui mettent en feu la discussion autour du patron de conception singleton:

  • n’utilise pas des abstract classes ou interfaces
  • pas de subclasses
  • difficile à tester
  • difficle de travailler parallèlement…

Quand même le singleton est souvent utilisé dans des projets grands et a sans doute une légitimation.

Design Patterns, les patrons de conception

Dans le domaine de l’informatique, et particulièrement dans le software engeneering, un << design pattern >> ou patron de conception est une solution générale ou bonne pratique en réponse à un problème habituel d’un logiciel.

Donc, un design pattern offre une solution standard qui peut être utilisé dans la conception (objet orienté) d’un logiciel. C’est-à-dire, un patron de conception a une influence sur l’architecture d’un logiciel.

Dans le livre << Design Patterns, Elements of Reusable Object-Oriented Software >> écrit par des auteurs americains, qui sont bien connu sous le nom GoF (Gang of four), il y a des différents types de design patterns :

  • Creational

Les patrons creational créent des objets pour vous, au lieu de vous laisser instancier des objets vous-même. Par exemple le Singleton pattern (unique instance of a class) ou le Factory pattern (object creats other objects).

  • Structural

Les patrons structural s’occupent d’une composition classe et objet. Ils utilisent l’inheritance et les interfaces pour créer de la nouvelle fonctionnalité. Exemple: Facade (provides a simplified interface to a large body o code) ou Adapter (makes classes with incomplete interfaces work together) pattern.

  • Behavioral

Les patrons behavioral s’occupent spécifiquement de la communication parmi les objets différents. Par exemple le Observer (used for events) ou le Strategy pattern (dependency injection).

Un aperçu de tout les patterns GoF peut être trouvé ici et puis, cette discussion sur stackoverflow montre les patrons de conception utilisés dans le codage C#.

En outre et en plus de patterns GoF, on peut jeter un coup d’oeil sur l’architecture d’une application et évoquer les patrons de conception architectural-style:

  • MVC (Model-View-Controller)
  • Repository (Seperation of concerns)
  • Service-locator (perhaps an anti-pattern, decouples classes from dependencies)

C’est aussi important, que la programmation objet orientée s’appuie sur le principe SOLID, si bien fait.

Néanmoins, malgré tout le << patron de conception >> le plus utilisé est probablement le Spaghetti code