Interface | La fabrique

Mon dernier article vous présentait les rudiments de base des interfaces et comment il pouvait être implémentés par des exemples en C#. Nous allons reprendre la découverte des interface à partir de ce moment pour aller plus loin en intégrant un patron de conception à notre apprentissage et en l’utilisant pour ajouter de la valeur à nos interface

Patron de conception : La fabrique

Tout d’abord, parlons de la fabrique. Alors ce patron de conception en fait sert à fabriquer des objets qui seront dérivés d’un autre. Généralement on parle de classes concrètes dérivées d’une classe abstraite. La classe qui utilise la fabrique pour se faire « fabriquer » un objet ne connaît pas d’avance quel objet lui sera retourné.

Cette fabrique peut aussi être utilisé pour fabriquer des objets qui ont un lien entre eux, mais pas nécessairement par polymorphisme, comme je l’explique dans le paragraphe précédent. Dans le cas qui nous intéresse ici, le lien qui les unis c’est l’interface que ces classes implémentent.

Intérêt avec notre interface

Comme nous avons vu dans notre exemple précédent, le fait de déclarer l’interface et d’y attacher un objet par la suite dans la même classe ne donne pas de valeur ajoutée. En fait, ça alourdis le code pour aucun bénéfice dans ce cas précis.

La fabrique, dans notre cas d’utilisation d’une interface, va servir à nous fabriquer un objet qui implémente l’interface à la classe qui utilise l’interface. Cette dernière ne sait pas comment la fabrique produit cet objet; elle sait seulement que cet objet joue le rôle décrit dans l’interface et c’est tout ce qu’elle doit savoir

L’intérêt c’est qu’avec un seul rôle à jouer et déterminé dans une interface, on peut distribuer ce rôle à un nombre de classes que l’on veut selon notre besoins. Par exemple, on peut avoir une classe utilisatrice SQL qui utilise une interface IDBClient et deux classes qui implémentent cette interface. Une utilise le client SQL natif de MS et l’autre ODBC. Donc, notre classe utilisatrice peut se connecter sur un serveur SQL en utilisant une ou l’autre selon le scénario. Et cela, sans changer l’implémentation dans la classe utilisatrice de l’interface IDBClient.

Mise en pratique avec notre exemple précédent

Alors, notre exemple précédent est ici et l’ensemble du code concernant les interface se trouve sur Github. Nous finissions notre exemple en déclarant une variable interface IRestClient et, dans la même ligne, y attribuons un objet RestClient qui implémente l’interface comme dans l’exemple ci-bas :

private IRestClient restClient = new RestClient();

Maintenant nous allons utiliser une fabrique pour créer l’objet.

Créer la fabrique

Généralement, dans ce genre de cas, on utilise une méthode statique dans une classe statique pour créer l’objet. Un paramètre est passé à la méthode pour indiquer un contexte et la fabrique crée l’objet selon ce paramètre. Alors, voici comment pourrait être notre fabrique :

namespace InterfacePlay
{
    public enum RestClientType { Real, Fake };

    public static class RestClientFactory
    {
        public static IRestClient CreateRestClient(RestClientType restClientType)
        {
            IRestClient restClient = new RestClientFake();
            switch (restClientType)
            {
                case RestClientType.Real:
                    restClient = new RestClient();
                    break;
            }
            return restClient;
        }
    }
}

Alors nous avons ici une classe statique RestClientFactory qui contient une méthode statique CreateRestClient qui prend en paramètre une énumération RestClientType et qui retourne une interface IRestClient. En fait, c’est plutôt un objet qui implémente l’interface qui est retourné.

Quand la méthode CreateRestClient est appelée, elle crée un objet selon la valeur du paramètre RestClientType qui lui est passé. C’est soit un RestClient ou un RestClientFake dans ce cas-ci.

Remarquez que, peut importe la classe qui est retournée, la condition pour ce faire doit être que la classe implémente l’interface.

Pourquoi le switch dans cette exemple et non pas un if? Parce que vous pouvez avoir besoin de ça si vos implémentez plus que deux classes et c’est par habitude que je l’ai mis ici dans une fabrique 🙂

Voyons maintenant comment utiliser la fabrique dans notre classe utilisatrice.

Utiliser l’interface

Voici donc la classe qui utilise l’interface IRestClient et voyons comment la fabrique est utilisée pour créer un objet et ainsi aider à attribuer cet objet à la variable IRestClient

using System.Net;

namespace InterfacePlay
{
    public class ConsumerAvecFactory
    {
        private const string request = "http://127.0.0.1/queryThat?option=this";

        private IRestClient restClient;
        private ParameterSet _parameterSet;

        public ConsumerAvecFactory(ParameterSet parameterSet)
        {
            _parameterSet = parameterSet;
            InitializeComponent(parameterSet);
        }

        public void SendRequest()
        {
            var restRequest = new RestRequest()
            {
                Request = request
            };
            restClient.AddRestRequest(restRequest);
        }

        public HttpStatusCode HttpStatusCode { get; set; } = HttpStatusCode.BadRequest;

        public string RestClientName { get; set; }

        private void InitializeComponent(ParameterSet parameterSet)
        {
            var restClientType = parameterSet.RestClientType;
            restClient = RestClientFactory.CreateRestClient(restClientType);

            restClient.RestServerResponded += (sender, restEvent) =>
            {
                HttpStatusCode = restEvent.HttpStatusCode;
                RestClientName = restEvent.RestClientName;
            };
        }
    }

    public class ParameterSet
    {
        public RestClientType RestClientType { get; set; }
    }
}

Alors, on a presque la même classe que dans l’article où l’on présente la base de l’utilisation des interfaces à la différence qu’ici l’objet qui implémente l’interface est créé dans la fabrique et que la classe qui se sert de l’interface IRestClient ne sait pas lequel sera retourné.

La ligne de code importante est la suivante et se trouve dans une méthode InitializeComponent appelée à partir du constructeur de la classe. Le paramètre qui sera passé à la fabrique et qui détermine quel objet sera retourné provient du constructeur. Dans un certain sens, on pourrait dire que la condition a été injectée dans le constructeur :

restClient = RestClientFactory.CreateRestClient(restClientType);

Les avantages

Utiliser une fabrique pour passer des objet qui implémente une interface qu’une classe utilise permet d’extensionner les possibilités d’une classe sans en changer une ligne de code et l’application elle même au final.

Par exemple, vous pouvez avoir une classe qui écrit la pensée du jour de l’utilisateur et qui prend cette valeur à travers une interface IClientInput implémentée par un classe qui lit directement à partir du clavier, une autre un répertoire en synchronisation avec le cloud et une autre un service web. Tout ça avec le même code dans la classe qui utilise une interface plutôt que des classes concrètes.

Pour aller plus loin

Au niveau des tests aussi cela donne vraiment beaucoup d’avantage, mais nous verrons cette partie un peu plus tard ainsi que l’injection de dépendance que vous aimerez vraiment beaucoup vous aussi 🙂

Conclusion

Nous avons vue qu’est-ce que le patron de conception de la fabrique. Ensuite, nous avons expliqué comment il pouvait servir avec les interfaces. Nous avons mis cet apprentissage en pratique et amélioré la classe qui utilise l’interface IRestClient que nous avons vu dans l’article précédent et nous avons vue les avantages d’utiliser la fabrique avec les interfaces.

Le prochain article portera encore sur les interfaces, c’est un sujet passionnant et on commence seulement à en faire le tour.

N’hésitez-pas à me laisser des commentaires ou me poser des questions si le cœur vous en dit, je suis toujours ouvert pour discuter et même me faire remettre à l’ordre 🙂