Patron de méthode
Intention
Patron de Méthode est un patron de conception comportemental qui permet de mettre le squelette d’un algorithme dans la classe mère, mais laisse les sous-classes redéfinir certaines étapes de l’algorithme sans changer sa structure.
Problème
Imaginez que vous êtes en train de créer une application de data mining (exploration de données) qui analyse les documents d’une entreprise. Les utilisateurs alimentent l’application avec différents formats (PDF, DOC, CSV) et celle-ci tente de récupérer les données utiles dans un format uniforme.
La première version de l’application ne fonctionnait qu’avec les fichiers DOC. Dans la version suivante, les fichiers CSV étaient acceptés. Un mois plus tard, vous lui avez « appris » à récupérer les données des fichiers PDF.
Au bout d’un moment, vous remarquez que ces trois classes comportent beaucoup de code similaire. Bien que le code qui gère les différents formats de données soit complètement différent d’une classe à l’autre, celui qui traite et analyse les données est presque identique. Ne serait-ce pas super de se débarrasser de tout ce code dupliqué tout en laissant la structure de l’algorithme intact ?
Un autre problème se pose avec le code client qui utilise ces classes. Il y a de gros blocs conditionnels qui choisissent un comportement en fonction de la classe de l’objet traité. Si ces trois classes avaient une interface commune (ou une classe de base), vous pourriez enlever toutes les conditions du code client et utiliser le polymorphisme lorsque vous appelez les méthodes sur un objet à traiter.
Solution
Le patron de méthode vous propose de découper un algorithme en une série d’étapes, de transformer ces étapes en méthodes et de mettre l’ensemble des appels à ces méthodes dans une seule méthode socle, le patron de méthode. Les étapes peuvent être abstraites
ou avoir une implémentation par défaut. Pour utiliser l’algorithme, le client doit fournir sa propre sous-classe, implémenter toutes les étapes abstraites et redéfinir certaines d’entre elles si besoin (mais pas la méthode socle elle-même).
Mettons ceci en application dans notre logiciel de data mining. Nous pouvons créer une classe de base pour les trois algorithmes d’analyse syntaxique (parsing). Cette classe définit une méthode socle, composée d’une série d’appels à plusieurs étapes qui traitent les documents.
En premier lieu, nous pouvons déclarer toutes les étapes abstraites
afin de forcer les sous-classes à définir leur propre implémentation pour ces méthodes. Dans notre cas, les sous-classes ont déjà les implémentations nécessaires. Nous devons donc uniquement ajuster les signatures des méthodes en les faisant correspondre à celles de la classe mère.
À présent, voyons ce que l’on peut faire pour se débarrasser du code dupliqué. Il semblerait que le code pour ouvrir/fermer les fichiers et récupérer/parser les données est différent pour chaque format de données, il n’y a donc aucune raison de toucher à ces méthodes. En revanche, les autres étapes (analyser les données brutes et établir les rapports) se ressemblent de près et leur implémentation peut donc être déplacée dans la classe de base, où les sous-classes se partagent le code.
Comme vous pouvez le voir, nous avons deux types d’étapes :
- les opérations abstraites qui doivent être implémentées dans chaque sous-classe.
- les opérations facultatives qui possèdent déjà une implémentation par défaut, mais peuvent être redéfinies si besoin.
Toutefois, un autre type d’étape existe : les crochets (hooks). Un crochet est une étape facultative dont le corps de méthode est laissé vide. Un patron de méthode peut fonctionner même si un crochet n’est pas redéfini. En général, les crochets sont placés avant ou après les étapes cruciales des algorithmes et procurent aux sous-classes des points d’extension supplémentaires.
Analogie
Le patron de méthode peut être utilisé pour construire des maisons en série. Le plan architectural pour construire une maison standard peut être doté de plusieurs points d’extensions qui permettent à un propriétaire potentiel d’ajuster certains détails dans la maison.
Chaque étape de construction (poser les fondations, poser la charpente, monter les murs, installer la plomberie pour l’eau et les câbles pour l’électricité, etc.) peut être légèrement modifiée pour différencier un peu la maison des autres.
Structure
-
La Classe Abstraite déclare des méthodes (qui représentent les étapes d’un algorithme) et la méthode patronDeMéthode qui appelle toutes ces méthodes dans un ordre spécifique. Les étapes peuvent être déclarées abstraites ou posséder une implémentation par défaut.
-
Les Classes Concrètes peuvent redéfinir toutes les étapes, mais pas patronDeMéthode.
Pseudo-code
Dans cet exemple, le patron de conception Patron de Méthode fournit un squelette pour les branches de l’IA (intelligence artificielle) d’un jeu vidéo de stratégie simple.
Toutes les races du jeu ont à peu près les mêmes unités et bâtiments. Vous pouvez donc réutiliser la même structure d’IA pour les différentes races, tout en personnalisant certains détails. Grâce à cette approche, vous pouvez redéfinir l’IA des orcs pour la rendre plus agressive, obtenir des humains plus défensifs, et faire en sorte que les monstres ne puissent pas construire de bâtiments. Pour ajouter une autre race au jeu, vous devez simplement créer une nouvelle sous-classe d’IA et redéfinir les méthodes par défaut déclarées dans la classe de base de l’IA.
Possibilités d’application
Utilisez le patron de méthode si vous voulez que vos clients puissent étendre des étapes spécifiques d’un algorithme, mais pas l’algorithme entier ou sa structure.
Le patron de méthode vous permet de transformer un algorithme monolithique en une série d’étapes individuelles qui peuvent être facilement étendues par des sous-classes, tout en gardant intacte la structure établie dans une classe mère.
Utilisez ce patron si vous avez plusieurs classes qui contiennent des algorithmes presque identiques, avec seulement quelques différences mineures. Par conséquent, vous risqueriez de devoir retoucher toutes les classes lorsque vous modifiez l’algorithme.
Lorsque vous transformez un tel algorithme en un patron de méthode, vous pouvez également remonter les étapes dotées d’implémentations similaires dans la classe mère, afin d’éviter la duplication de code. Vous pouvez laisser le reste du code dans les sous-classes.
Mise en œuvre
-
Analysez l’algorithme ciblé pour voir si vous pouvez le décomposer en étapes. Déterminez les étapes communes à toutes les sous-classes et celles qui sont uniques.
-
Créez une classe de base abstraite et déclarez le patron de méthode et un ensemble de méthodes abstraites pour représenter les opérations de l’algorithme. Faites une ébauche de la structure de l’algorithme dans ce patron de méthode en appelant les opérations correspondantes. Rendez ce patron
final
pour empêcher les sous-classes de la redéfinir. -
Cela ne pose aucun problème si toutes les opérations sont abstraites, mais une implémentation par défaut bénéficierait à certaines opérations. Les sous-classes n’ont pas besoin d’implémenter ces méthodes.
-
Pensez à ajouter des crochets entre les étapes cruciales de votre algorithme.
-
Pour chaque variante de l’algorithme, créez une nouvelle sous-classe. Elle doit implémenter toutes les opérations abstraites, mais peut également redéfinir les opérations facultatives.
Avantages et inconvénients
- Vous permettez aux clients de redéfinir certaines parties d’un grand algorithme. Elles sont ainsi moins affectées par les modifications apportées aux autres parties de l’algorithme.
- Vous pouvez remonter le code dupliqué dans la classe mère.
- Certains clients peuvent être limités à cause du squelette de l’algorithme.
- Vous ne respectez pas le Principe de substitution de Liskov, si vous supprimez l’implémentation d’une étape par défaut dans une sous-classe.
- Plus vous avez d’étapes, plus le patron de méthode devient difficile à maintenir.
Liens avec les autres patrons
-
La Fabrique est une spécialisation du Patron de méthode. Une fabrique peut aussi faire office d’étape dans un grand patron de méthode.
-
Le Patron de méthode est basé sur l’héritage : il vous laisse modifier certaines parties d’un algorithme en les étendant dans les sous-classes. La Stratégie est basée sur la composition : vous pouvez modifier certaines parties du comportement de l’objet en lui fournissant différentes stratégies qui correspondent à ce comportement. Le patron de méthode agit au niveau de la classe, il est donc statique. La stratégie agit au niveau de l’objet et vous laisse permuter les comportements à l’exécution.