Template Method
Design Pattern Template Method
Moins de duplication, plus de maintenabilité
Introduction
Template Method est un design pattern de comportement. Son objectif est de définir le squelette d'un algorithme dans une classe de base, tout en laissant certaines étapes être spécialisées par les classes dérivées.
Ce design pattern est particulièrement adapté lorsque plusieurs traitements suivent un ensemble d'étapes communes, mais diffèrent uniquement dans certaines d'entre elles.
Problématique
Prenons l'exemple d'une application capable d'importer des données provenant de plusieurs formats de fichiers. Quel que soit le format, les étapes sont toujours les mêmes : lecture du fichier, puis analyse des données, puis validation et enfin enregistrement. Cependant, l'analyse dépend du format du fichier.
Sans implémenter le design pattern Template Method, nous pouvons implémenter plusieurs classes contenant du code presque identique.
public class CsvImporter
{
public void Import(string fileName)
{
string content = File.ReadAllText(fileName);
Console.WriteLine("Analyse du fichier CSV");
Console.WriteLine("Validation");
Console.WriteLine("Enregistrement");
}
}
public class JsonImporter
{
public void Import(string fileName)
{
string content = File.ReadAllText(fileName);
Console.WriteLine("Analyse du fichier JSON");
Console.WriteLine("Validation");
Console.WriteLine("Enregistrement");
}
}
Les deux classes réalisent pratiquement le même traitement. Seule la phase qui présente le type d'analyse diffère et cette duplication nuit à la maintenance du code.
Conception
Le diagramme ci-dessous illustre les différents participants du design pattern.
Chaque classe joue un rôle précis :
-
La classe DataImporter représente la classe abstraite.
-
La méthode Import() constitue la Template Method.
-
La méthode Parse() représente une étape spécialisée par les classes dérivées.
-
Les classes CsvImporter et JsonImporter implémentent les étapes spécifiques.
Implémentation
Création de la classe abstraite
public abstract class DataImporter
{
public void Import(string fileName)
{
string content = Read(fileName);
Parse(content);
Validate();
Save();
}
protected virtual string Read(string fileName)
{
return File.ReadAllText(fileName);
}
protected abstract void Parse(string content);
protected virtual void Validate()
{
Console.WriteLine("Validation");
}
protected virtual void Save()
{
Console.WriteLine("Enregistrement");
}
}
La méthode Import() définit les traitements communs pour tous les imports et représente la Template Method. Certaines étapes sont communes (actions réalisées par les méthodes Read, Validate et Save), tandis que la méthode Parse est spécifique et chaque classe dérivées doit présenter une implémentation.
Implémentation des classes d'importation
Voici l'implémentation des classes d'importation, héritant de la classe DataImporter et redéfinissant le traitement spécifique de parsing qui leur est propre :
public class CsvImporter : DataImporter
{
protected override void Parse(string content)
{
Console.WriteLine("Analyse du fichier CSV");
}
}
public class JsonImporter : DataImporter
{
protected override void Parse(string content)
{
Console.WriteLine("Analyse du fichier JSON");
}
}
Utilisation
En fonction du type de fichier que nous devons importer, nous instancions la classe correspondante :
DataImporter importer = new CsvImporter();
importer.Import("customers.csv");
DataImporter importer = new JsonImporter();
importer.Import("customers.json");
Ajout d'un nouveau type d'import
Supposons que nous devions importer un nouveau type de fichier : des fichiers au format XML. Il nous suffit d'implémenter une nouvelle classe dérivant de la classe DataImporter qui redéfinit la méthode Parse afin d'implémenter l'analyse spécifique propre à ce nouveau type de fichier :
public class XmlImporter : DataImporter
{
protected override void Parse(string content)
{
Console.WriteLine("Analyse du fichier XML");
}
}
Aucune modification n'est nécessaire dans la classe DataImporter. Ainsi, le code reste ainsi ouvert à l'extension et fermé à la modification, conformément au principe SOLID Open/Closed (OCP).
