DotnetDojo

Développer des applications modernes avec les technologies Microsoft et Open source

  • Blog
  • Vidéos
  • Formations
  • Outils

2 méthodes pour écrire du code plus facile à faire évoluer

Button idea bulb business web
Je souhaiterais vous parler de 2 techniques très simples, qui permettent d’écrire du code plus facile à faire évoluer.
L’idée est de respecter le principe d’ouvert-fermé (OCP).

Pour mémoire, le principe OCP dit qu’un morceau de code doit être ouvert aux évolutions, mais fermé aux modifications.
Il s’agit donc de pouvoir faire évoluer du code sans toucher directement à ce code. Pour en savoir plus sur les bases du principe, je vous invite à consulter l’article Concevoir des applications SOLID avec OCP.

Habituellement, lorsque vous codez une application, vous écrivez des conditions et des cas avec un nombre assez limité.
Vous commencez à écrire une condition (bloc if) et celle-ci à seulement 3 cas possibles (if, else if, else if). Tout se passe bien.

Seulement voilà, un peu plus tard (parfois quelques jours), les spécifications changent et votre responsable vous demande d’ajouter des cas supplémentaires pour cette fameuse condition.
Naturellement, vous modifiez donc le code qui concerne chaque nouveau cas. C’est une manière de faire tout à faire naturelle mais pas du tout OCP.

Le problème avec cette manière de faire est simple : si vous devez modifier votre code (un peu partout), il y a des risques pour que vous introduisiez aussi de nouveaux bugs ou dysfonctionnements.

Je vous propose donc de découvrir deux techniques simples pour écrire du code qui peut évoluer sans plus jamais toucher à ce morceau de code.

J’ai déjà parlé plusieurs fois de l’utilisation des délégués ou des événements pour écrire du code OCP. Aujourd’hui, je vous propose deux autres techniques différentes.

Technique n° 1 : Faire évoluer le code avec un dictionnaire

L’idée est simple : utiliser un dictionnaire du type clé-valeur pour réécrire les conditions et le bloc if.

Le dictionnaire sera rempli de la manière suivante :

  • La clé contient la valeur du cas à tester
  • La valeur contient une référence vers une méthode à exécuter (ou expression lambda)

Je vous propose un exemple pour mieux comprendre.

Voici le code (avant transformation):
[csharp] if (type == 1)
{
Console.WriteLine("Type 1");
}
else
if (type == 2)
{
Console.WriteLine("Type 2");
}
else
{
Console.WriteLine("Autres types");
}
[/csharp]

Ce code contient un bloc if avec trois conditions. Nous aurions aussi pu utiliser un switch/case (ça fonctionne de la même manière).

Voici le code après utilisation d’un dictionnaire :
[csharp] // (partie 1) initialisation : différents cas
Dictionary<int, Action> relations = new Dictionary<int, Action>();

// cas 1 : définition de l’action (expression lambda)
relations[1] = () => Console.WriteLine("Type 1");
// cas 2
relations[2] = () => Console.WriteLine("Type 2");

int type = 1;

// (partie 2)
Action action;
if (relations.TryGetValue(type, out action))
{
action();
}
else
{
Console.WriteLine("Autres types");
}
[/csharp]

Ce code est découpé en deux parties :

  • La première partie concerne la mise en place du dictionnaire. On ajoute des actions en fonction des différents cas.
  • La deuxième partie concerne la récupération de l’action en fonction du cas. TryGetValue permet de gérer le cas par défaut (la clé n’existe pas dans le dictionnaire).

Comme vous le voyez, cette technique est astucieuse car elle utilise un dictionnaire pour stocker des références vers des actions (méthodes).
Elle permet d’ajouter très simplement un nouveau cas sans modifier le bloc if (pour cela, il suffit d’ajouter une ligne du type relations[3] = …).

Dans cet exemple, le code est très simple. Il faut bien savoir, qu’en général, les bloc if sont plus complexes que ça et c’est donc là qu’il y a vraiment un intérêt à utiliser ce type de mécanisme.

De plus, le dictionnaire pourra aussi être réutilisé pour une autre méthode par exemple. Cela permet donc d’éviter de dupliquer le bloc if.

Technique n° 2 : Faire évoluer le code avec l’héritage

La deuxième technique fonctionne un peu de la même manière. L’idée est de remplacer le bloc if par des classes utilisant l’héritage.

Il y a différentes manières d’utiliser l’héritage pour écrire du code plus évolutif. Je vous propose un exemple qui reprend le même type d’exemple que pour la technique 1.

[csharp] static void Main(string[] args)
{
double tarifInterne = 12;
string typeClient = "Normal";

double tarif = CalculerTarif(tarifInterne, typeClient);
Console.WriteLine("Le tarif est : {0}", tarif);
}

static double CalculerTarif(double tarif, string typeClient)
{
switch (typeClient)
{
case "Normal":
return tarif * 1.1;
case "VIP":
return tarif * 0.8;
default:
return tarif;
}
}
[/csharp]

Ce code permet de calculer le tarif à partir d’un tarif interne et du type de client. C’est simple et efficace.
Mais, que pourrions-nous faire si nous devions ajouter d’autres types de clients ? Étendre le switch est une solution, mais il y a aussi la solution d’utiliser l’héritage.

Une autre manière de faire, serait de créer une classe « Client » qui contient une méthode CalculerTarif. La responsabilité du calcul sera alors transférée à la classe Client.

Je vous propose un exemple de code transformé :

[csharp] class Program
{
static void Main(string[] args)
{
double tarifInterne = 12;

// Créer un client (en fonction du type avant transformation)
Client client = new ClientNormal();

// Demander à la classe Client de calculer le tarif
double tarif = client.CalculerTarif(tarifInterne);

Console.WriteLine("Le tarif est : {0}", tarif);
}
}

/// <summary>
/// Client par défaut
/// </summary>
class Client
{
public virtual double CalculerTarif(double tarif)
{
return tarif;
}
}

/// <summary>
/// Client "normal"
/// </summary>
class ClientNormal : Client
{
public override double CalculerTarif(double tarif)
{
return tarif * 1.1;
}
}

/// <summary>
/// Client "VIP"
/// </summary>
class ClientVIP : Client
{
public override double CalculerTarif(double tarif)
{
return tarif * 0.8;
}
}
[/csharp]

Comme vous le voyez, cet exemple est plus long. Il demande (un peu) plus de travail. Par contre, vous pouvez très facilement ajouter un type de client sans modifier le code existant. C’est l’avantage de cette technique.

Et peut être que par la suite, votre classe Client pourra évoluer et proposer des méthodes complémentaires comme CalculerFraisDePort, CalculerPointsFidelite…

Cette technique est simple à mettre en oeuvre. Je vous conseille, par contre, de la mettre en place assez tôt dans le développement, sinon elle demandera plus de travail par la suite.

A vous de jouer

Désormais, c’est à vous d’essayer ! Essayez et faites-moi un retour en laissant un commentaire ci-dessous.

[ninja-inline id=3695]

Besoin de résultats rapides ?

Découvrez les formations vidéos que je propose :

  

 

Formations en présentiel (dans toute la France)

Découvrez également les formations C# et .NET que je donne en présentiel (en France)

Comments

  1. Thierry Carpentier says

    2 décembre 2014 at 23 h 26 min

    Bonjour,
    Dans le second exemple, pouvons-nous utiliser une interface IClient à la place de la classe Client ? le résultat est-il le même ?

    Merci également pour tous les excellents tuto.
    Thierry du Quebec

    • Pascal Lacroix says

      4 décembre 2014 at 10 h 17 min

      Bonjour Thierry,

      En fait, il est tout à faire possible d’utiliser une interface IClient mais ça ne va pas résoudre notre problème.
      Utiliser une interface va imposer un certain nombre de méthodes (par exemple la méthode CalculerTarif) mais une interface ne peut pas contenir de code.
      Dans ma solution, j’utilise une classe de base pour avoir un comportement par défaut, ce qui permet aux classes qui héritent de « Client » de choisir de surcharger ou pas la méthode CalculerTarif.

      Si vous souhaitez utiliser une interface, il faudra forcement définir la méthode CalculerTarif dans chaque classe.

      J’espère avoir répondu à votre question,
      Pascal

A propos de DotnetDojo

Pascal Lacroix

Je m’appelle Pascal et je suis passionné par le développement logiciel, l’efficacité et l’entrepreneuriat. Sur DotnetDojo, je vous propose des méthodes pour apprendre à développer des applications modernes avec les technologies Microsoft et Open Source.

En savoir plus

Liens complémentaires

  • A propos de DotnetDojo
  • 18 principes pour professionnaliser le développement logiciel
  • Boite à outils du développeur
  • Tous les articles
  • Liste des formations
  • Contact

Copyright 2019 Jupiteo · Mentions légales · Contact · CGV · Offres d'emploi .NET · Formations Dotnet