Accueil / Tutoriels / Template Method : structurer le comportement avec élégance
Template Method : structurer le comportement avec élégance
Publié le 23 Juin 2025
Template Method est un design pattern comportemental qui permet de définir le squelette d’un algorithme dans une méthode de classe, en laissant certaines étapes à la charge des sous-classes. Il favorise la réutilisabilité, réduit la duplication de code, et permet d'encadrer la logique métier dans des flux d'exécution standardisés.
Adopté dans de nombreux frameworks web, ce pattern est particulièrement efficace dans les cas où plusieurs classes suivent un déroulé similaire, mais avec quelques variations spécifiques à chaque implémentation. Voyons ensemble son intérêt, ses cas d’usage concrets, ses limites, et enfin comment l’implémenter efficacement.
Principe : factoriser sans brider
Le Template Method pattern repose sur le principe qu’une classe abstraite définit une méthode principale (le "template"), qui suit un enchaînement d’étapes. Certaines de ces étapes sont implémentées dans la classe de base, d’autres sont laissées à la charge des sous-classes via des méthodes abstraites ou surchargées.
Cela permet de figer la structure globale d’un processus tout en déléguant certains détails à des implémentations spécifiques. Le pattern favorise une inversion de contrôle maîtrisée, où le comportement global est imposé, mais les détails sont injectés par les sous-classes. Cela améliore la cohérence des traitements, tout en facilitant l’extensibilité.
Exemples d’utilisation du pattern Template Method
Génération de pages HTML avec des sections communes
Dans une application web générant dynamiquement des pages (via un moteur de template ou une API), on retrouve souvent une structure commune : entête, corps, pied de page. Pourtant, chaque type de page (accueil, article, profil utilisateur, etc…) nécessite des contenus différents dans le corps.
Le Template Method permet ici de définir une méthode renderPage()
dans une classe abstraite qui appelle successivement renderHeader()
, renderBody()
, et renderFooter()
. Seul renderBody()
est laissé à l’implémentation des sous-classes. Cela garantit une structure cohérente entre les pages, tout en permettant la personnalisation de leur contenu principal.
Pipeline de traitement d'une requête API
Lorsqu'une API traite des requêtes entrantes, certaines étapes sont toujours les mêmes : authentification, validation, traitement, formatage de la réponse. Pourtant, le cœur du traitement change d'une ressource à l'autre.
Le Template Method est ici un excellent choix pour définir une méthode handleRequest()
commune à tous les contrôleurs, avec des étapes standards (checkAuth, parsePayload, etc.), et une méthode process()
spécifique à chaque endpoint. Cela garantit la robustesse du pipeline tout en autorisant la spécialisation.
Points d’attention
Le principal inconvénient du pattern Template Method est dans sa dépendance à l’héritage. En imposant une hiérarchie entre une classe abstraite et ses sous-classes, il introduit un couplage fort qui peut devenir rigide, surtout dans des architectures modernes où la composition est préférée à l’héritage. Plus le nombre de sous-classes augmente, plus la hiérarchie peut devenir difficile à maintenir et à faire évoluer.
En dispersant la logique métier entre la classe de base et ses multiples déclinaisons, on peut rapidement perdre en clarté. Il devient parfois complexe de comprendre l’enchaînement exact des opérations sans naviguer entre plusieurs fichiers, ce qui nuit à la maintenabilité, notamment pour les développeurs qui découvrent le code.
Ce pattern suppose également que toutes les étapes définies dans la méthode template ont un sens dans chaque cas d’usage. Or, certaines sous-classes n’auront peut-être pas besoin d’implémenter toutes les étapes prévues. Cela pousse parfois à créer des méthodes vides ou à ajouter des vérifications conditionnelles, ce qui dilue la pureté du modèle et le rend moins élégant.
Enfin, dans des contextes où les étapes du processus sont susceptibles d’évoluer de manière indépendante, le Template Method peut devenir une contrainte. Modifier l’ordre ou la nature des étapes impose souvent de modifier la classe de base, ce qui peut avoir un effet domino sur toutes les sous-classes existantes. Ce manque de souplesse peut poser problème dans des projets en constante évolution ou dans des équipes nombreuses.
Implémenter le pattern Template Method
Pour mettre en œuvre le Template Method, on commence par définir une classe de base (généralement abstraite) qui contient une méthode principale décrivant le déroulement global du processus. Cette méthode orchestre les différentes étapes de l’algorithme en appelant plusieurs sous-méthodes, chacune correspondant à une phase du traitement.
La méthode principale doit rester inchangée dans les sous-classes. Pour éviter toute surcharge ou altération involontaire, il est courant de la déclarer comme final (ou avec un équivalent, selon le langage). Cela signifie qu’elle ne pourra pas être redéfinie, garantissant ainsi que la structure globale du processus reste intacte, quelle que soit la sous-classe qui l’utilise. En revanche, les méthodes appelées à l’intérieur de ce squelette peuvent être abstraites (obligeant leur implémentation) ou bien facultatives, selon le besoin.
Ce pattern fonctionne particulièrement bien dans les situations où un processus métier suit un enchaînement stable et prévisible, mais nécessite quelques adaptations spécifiques dans certaines étapes, d’où l’intérêt de déléguer ces points précis aux sous-classes.
Voici un exemple en Go :
Imaginons une plateforme web qui envoie différents types de notifications : email de bienvenue, alerte de sécurité, résumé hebdomadaire. Chaque notification suit le même schéma : chargement du destinataire, construction du message, envoi. Mais le contenu varie selon le type de notification.
Le processus global d’envoi de notification est figé dans la méthode send()
. Seules les étapes spécifiques (chargement, construction du message, envoi) sont personnalisées. Cela permet d’avoir une logique centralisée, testable et facilement extensible à d’autres types de notifications (alerte, rappel, confirmation...).
Template Method, dans sa forme classique orientée objet, ne s’intègre pas naturellement dans le paradigme Go, qui est orienté composition, pas héritage. En Go, le vrai Template Method devient souvent une fonction qui appelle une série d'étapes définies par une interface.
package main
import "fmt"
// Notification Interface Notification définit les étapes à implémenter
type Notification interface {
LoadRecipient()
BuildMessage()
SendMessage()
}
// BaseNotification contient la méthode template
type BaseNotification struct{}
func (BaseNotification) Send(n Notification) {
n.LoadRecipient()
n.BuildMessage()
n.SendMessage()
}
// WelcomeNotification implémente l'interface Notification
type WelcomeNotification struct{}
func (WelcomeNotification) LoadRecipient() {
fmt.Println("Chargement de l'utilisateur nouvellement inscrit.")
}
func (WelcomeNotification) BuildMessage() {
fmt.Println("Construction du message de bienvenue.")
}
func (WelcomeNotification) SendMessage() {
fmt.Println("Envoi de l'email de bienvenue.")
}
func main() {
welcome := WelcomeNotification{}
base := BaseNotification{}
base.Send(welcome)
}
Le même exemple en PHP :
namespace Practice\DesignPatterns\TemplateMethod;
abstract class Notification {
public function send(): void {
$this->loadRecipient();
$this->buildMessage();
$this->sendMessage();
}
abstract protected function loadRecipient(): void;
abstract protected function buildMessage(): void;
abstract protected function sendMessage(): void;
}
namespace Practice\DesignPatterns\TemplateMethod;
class WelcomeNotification extends Notification {
protected function loadRecipient(): void {
echo "Chargement de l'utilisateur nouvellement inscrit.\n";
}
protected function buildMessage(): void {
echo "Construction du message de bienvenue.\n";
}
protected function sendMessage(): void {
echo "Envoi de l'email de bienvenue.\n";
}
}
declare(strict_types=1);
use Practice\DesignPatterns\TemplateMethod\WelcomeNotification;
require "./vendor/autoload.php";
$notif = new WelcomeNotification();
$notif->send();
Dans ces deux exemples, le processus global d’envoi de notification est figé dans la méthode send().
Seules les étapes spécifiques (chargement, construction du message, envoi) sont personnalisées. Cela permet d’avoir une logique centralisée, testable et facilement extensible à d’autres types de notifications (alerte, rappel, confirmation...).
Conclusion
Le Template Method est un allié précieux lorsqu’on veut normaliser des processus tout en laissant place à la variation. Il favorise la réutilisation, la lisibilité des workflows, et impose un cadre qui réduit les erreurs.
Ce pattern s’intègre naturellement dans les projets web modernes, qu’il s’agisse de générer des vues cohérentes, d’unifier les flux API ou encore de normaliser des tâches récurrentes.
J'espère que grâce à cet article vous êtes désormais plus à l'aise avec le design pattern Template Method. Vous pouvez retrouver mon article sur les patrons de conception ou les autres tutoriels sur Composite, Memento, Bridge, Flyweight et Builder.