Template method

La programmation avec design patterns

Ce site ne sera plus alimenté de contenu après août 2014. Tous les nouveaux articles seront redigés pour www.waitingforcode.com
Les fondations d'un élément sont intouchables, aussi bien dans le monde réel que dans le développement informatique. Un des design patterns veille à ce qu'elles le restent. Il s'agit du template method.

Template method (méthode modèle) est un patron de conception qui empêche les sous-classes de changer le comportement de certaines méthodes. Ces méthodes, appelées modèles, sont d'habitude finales et donc, par définition, rendent impossible leur surcharge. Grâce à cela, on peut très facilement définir une squelette de l'algorithme en déléguant uniquement ses fragments à des sous-classes.

Ce patron de conception facilite la maintenance du code. Imaginons la situation où l'on a une classe de lecture des fichiers avec la méthode getStringContentFromFile(). Elle récupère le contenu textuel du fichier. Maintenant elle utilisera 3 méthodes abstraites: openFile(), contentFromByteToString() et closeFile() pour récupérer un String contenu dans le fichier donné. Ces 3 méthodes seront définis dans la méthode getStringContentFromFile() qui sera en même temps finale. C'est cette méthode qui est considérée comme template method - elle définit un comportement et laisse l'implémentation aux sous-classes.

Pour voir encoire mieux la différence d'une méthode modèle avec d'autres, listons de différents types de méthodes :
- concrete methods (méthodes concrètes) : les méthodes classiques, étant souvent des méthodes utilitaires, communes à toutes les classes (comme par exemple la lecture d'un fichier, l'ouverture d'une connexion à la base de données). Elles ne sont pas destinées à être surchargées.
- abstract methods (méthodes abstraites) : les méthodes sans implémentations qui doivent être surchargées dans les sous-classes.
- hook methods (méthodes de surcharge) : contrairement à des méthodes concrètes, ces méthodes contiennent une implémentation précise censée être surchargée par des sous-classes.
- template methods : les méthodes qu'on présente dans cet article. Il s'agit donc des méthodes qui permettent de décrire un algorithme, sans pour autant préciser les détails d'implémentation. Cela peuvent être aussi bien les méthodes abstraites que concrètes. Ces premières rendent possible la personnalisation du traitement par les sous-classes tandis que les dernières s'assurent que le traitement suivra un traitement prévu.

Template method décrit donc l'algorithme en lui spécifiant les points finaux aussi bien que les points abstraits qui nécessitent une déclaration adaptée à des sous-classes.

Exemple du template method
Afin de mieux comprendre les règles de ce patron de conception, on utilisera l'exemple d'un immeuble. La construction de cet immeuble se fera selon 3 étapes : la création des fondations, la construction de l'extérieur et, finalement, des travaux à l'intérieur. La seule différence entre les deux bâtiments sera le nombre d'étages. Le premier en aura 4 tandis que l'autre que 10.

abstract class Building {
public final void construct() {
makeFoundations();
constructOutside();
constructInside();
}
private abstract void makeFoundations();
private abstract void constructOutside();
private abstract void constructInside();

}

C'est notre template method. L'algorithme est décrit des 3 étapes déjà mentionnées, dont une ne changera jamais (makeFoundations()) et deux dépendront de l'implémentation (bâtiment de 4 étages et un autre des 10).


class FourFloorsBuilding extends Building {

@Override
private void makeFoundations() {
System.out.println("For every building we start by making the foundations.");
}

@Override
private void constructOutside() {
System.out.println("We construct only 4 floors");
}

@Override
private void constructInside() {
System.out.println("We have only 4 floors. We don't need to add elevator. The stairs suffice");
}

}

class TenFloorsBuilding extends Building {

@Override
private void makeFoundations() {
System.out.println("For every building we start by making the foundations.");
}

@Override
private void constructOutside() {
System.out.println("We construct a big building with 10 floors");
}

@Override
private void constructInside() {
System.out.println("Ten floors require elevator and stairs");
}
}

Ce sont les deux classes qui implémentent l'algorithme défini dans la classe abstraite Building.



System.out.println("Four floors building");
Building fourFloors = new FourFloorsBuilding();
fourFloors.construct();
System.out.println("Ten floors building");
Building tenFloors = new TenFloorsBuilding();
tenFloors.construct();

Le code du client qui utilise le template method se présence comme ci-dessus. On ne remarque pas les différences fondamentales entre les deux bâtiments. La grande différence qu'on pourra remarquer serait l'absence de surcharge de la méthode finale (impossible par définition) qu'on utilise pour constuire une maison.En occurence, on pourrait encore gérer les états et, par exemple, ne pas permettre d'appeler constructInside() avant constructOutside(). Pour gérer cela, on pourrait employer le design pattern state.


Four floors building
For every building we start by making the foundations.
We construct only 4 floors
We have only 4 floors. We don't need to add elevator. The stairs are enough
Ten floors building
For every building we start by making the foundations.
We construct a big building with 10 floors
Ten floors require elevator and stairs

Juste pour la formalité, ci-dessus le résultat de notre traitement.

Le patron de conception qu'on vient de présenter est très utile dans les situations où l'on veut contrôler à tout prix l'algorithme. Couplé avec d'autres design patterns, comme state, il permet de s'assurer que l'algorithme est invoqué selon la logique programmée.
Bartosz KONIECZNY 09-03-2014 17:05 design patterns
Moi

Développeur d'applications Internet et journaliste passionné par l'adjectif français. Un aigle polonais orienté vers la progression, volant très haut et écoutant du zouk après les matches du foot français.

Vous appréciez mon travail ?

Pour contribuer au développement de ce site, ou pour remercier pour des articles rédigés, vous pouvez faire un don.

Un conseil Symfony1

Quelle méthode il faut utiliser afin de récupérer les paramètres d'une route dans la classe sfRoute ?

Imaginons la situation où l'on veut créer une nouvelle classe pour gérer les routes URL de notre application. Afin de faire cela, on devrait hériter de la classe sfRoute.

Les paramètres d'une requête peuvent être lus grâce à la méthode matchesUrl() de sfRoute. Par exemple :
public function matchesUrl($url, $context = array()) {
  $parameters = parent::matchesUrl($url, $context);
  if(count($parameters) > 1) {
    echo 'Vous pouvez faire ce que vous voulez avec les paramètres reçus';
  }
}