Builder

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
La création de certains objets est vraiement compliquée et nécessite beaucoup d'opérations. Qui dit beaucoup, celui pense immédiatement à une difficulté de maintenance d'un tel code. Pour la faciliter un design pattern a été défini. Il s'appelle builder.

Une bonne illustration qui permet de comprendre à quoi sert ce design pattern est l'organisation de vacances. Pour les besoins de cet exemple on suppose qu'elle se déroule toujours de la même manière. Dans un premier temps on choisit le pays de visite. Ensuite on remplit les formalités nécessaires pour pouvoir y entrer. Après on prépare nos bagages. A la fin on monte au bord d'un avion et on vole jusqu'à la destination finale. Transmettons cela dans le modèle informatique, pour l'instant sans y appliquer le patron de conception builder.

Composition du design pattern Builder

abstract class HolidaysCreator {
protected Holidays holidays;

public void makeNewHolidays() {
this.holidays = new Holidays();
}

public String getCreatedHolidays() {
return this.holidays.toString();
}

public abstract void chooseCountry(String country);
public abstract void doFormality(String formality);
public abstract void pack(String luggage);
public abstract void board(String airplane);
}

class FranceHolidaysCreator extends HolidaysCreator {

@Override
public void chooseCountry(String country) {
this.holidays.setCountry("France");
}

@Override
public void doFormality(String formality) {
this.holidays.setFormality("No formalities to EU residents");
}

@Override
public void pack(String luggage) {
this.holidays.setLuggage("one lugagge");
}

@Override
public void board(String airplane) {
this.holidays.setAirplane("we go in car");
}
}


class UsaHolidaysCreator extends HolidaysCreator {

@Override
public void chooseCountry(String country) {
this.holidays.setCountry("The United States of America");
}

@Override
public void doFormality(String formality) {
this.holidays.setFormality("I need the touristic visa");
}

@Override
public void pack(String luggage) {
this.holidays.setLuggage("Two big luggages");
}

@Override
public void board(String airplane) {
this.holidays.setAirplane("Boeing");
}

}

Une classe abstraite, HolidaysCreator, sert à déterminer les étapes que chaque voyage doit contenir. On y retrouve ce qu'on a mentionné au début de l'article : choix du pays (chooseCountry()), la réalisation des formalités (doFormality()), la préparation des baggages (pack()) ainsi que l'embarquement (board()). Les deux classes héritant de HolidaysCreator déterminent plus précisement les étapes qu'il faut accomplir pour partir en France (FranceHolidaysCreator) et aux Etats-Unis (UsaHolidaysCreator).

Si l'on transpose ces deux types de classes dans le Builder, on connaîtra deux premiers acteurs de ce patron. La classe abstraite (ici HolidaysCreator), dans Builder s'appelle... builder. Il s'agit de l'interface abstraite qui détermine quelles étapes sont à effectuer pour créer l'objet final. Ses deux implémentations (FranceHolidaysCreator et UsaHolidaysCreator) sont dans le monde de ce patron de conception, des concrete builders (constructeurs concrets). Ce sont elles qui réalisent les étapes et créent l'objet final.


class Holidays {

private String country;
private String formality;
private String luggage;
private String airplane;

public void setCountry(String country) {
this.country = country;
}

public void setFormality(String formality) {
this.formality = formality;
}

public void setLuggage(String luggage) {
this.luggage = luggage;
}

public void setAirplane(String airplane) {
this.airplane = airplane;
}

@Override
public String toString() {
StringBuilder repr = new StringBuilder("Holidays resume : ");
repr.append("- country : ").append(this.country);
repr.append("\n-formalities :").append(this.formality);
repr.append("\n-luggage : ").append(this.luggage);
repr.append("\n-airplane : ").append(this.airplane);
return repr.toString();
}
}

Cette classe caractérise le voyage qu'on souhaite organiser. Dans le monde du Builder elle s'appelle product et représente l'objet qui est construit par les concrete builders. Le processus de sa création est donc totalement transparent au développeur qui regarde cette classe. Cependant, le design pattern Builder possède encore un acteur important. Le voici :

class Tourist {
private HolidaysCreator holidays;

public Tourist(HolidaysCreator holidays) {
this.holidays = holidays;
}

public String getMakedHolidays() {
return this.holidays.getCreatedHolidays();
}

public void makeHolidays() {
this.holidays.makeNewHolidays();
this.holidays.chooseCountry();
this.holidays.doFormality();
this.holidays.pack();
this.holidays.board();
}

}

Le touriste, c'est-à-dire la personne qui choisit ses vacances, joue le rôle d'un director dans le sens de ce design pattern. C'est lui qui contrôle le processus de création des vacances. En occurence, cela se traduit par l'appel synchronisé des méthodes du concrete builder passé en paramètre dans le constructeur.

Et maintenant le code qui fait marcher tout ensemble :

HolidaysCreator franceHolidays = new FranceHolidaysCreator();
Tourist tourist = new Tourist(franceHolidays);
tourist.makeHolidays();
System.out.println("Maked holidays : ");
System.out.println(tourist.getMakedHolidays());


Sur l'écran on verra :

Maked holidays :
Holidays resume : - country : France
-formalities :No formalities to EU residents
-luggage : one lugagge
-airplane : we go in car


Cependant, l'exemple ci-dessus peut paraître le même que d'autres design patterns (template method par exemple). Pour bien comprendre l'idée, on montrera un autre exemple du builder, plus souvent rencontré dans les applications. Il consiste à utiliser une classe privée pour construire l'objet qui l'englobe. On utilisera cette écriture pour montrer une classe stockant les coûts des vacances. Vu que les vacances peuvent avoir plusieurs frais, cette classe est amenée à évoluer très souvent en y rajoutant ou supprimant les champs.Transmettons cela dans le modèle informatique, pour l'instant sans y appliquer le patron de conception builder.


public class Builder {

public static void main(String[] args) {
Holidays holidays = new Holidays.HolidaysBuilder().setBSportCost(300).setBTicketCost(200).setBTransportCost(100).build();
System.out.println("Holidays are: "+holidays);
}

}


class Holidays {
private int sportCost;
private int ticketCost;
private int transportCost;

private Holidays(int sport, int ticket, int transport) {
this.sportCost = sport;
this.ticketCost = ticket;
this.transportCost = transport;
}

public static class HolidaysBuilder {
private int bSportCost;
private int bTicketCost;
private int bTransportCost;

public HolidaysBuilder setBSportCost(int sport) {
this.bSportCost = sport;
return this;
}
public HolidaysBuilder setBTicketCost(int ticket) {
this.bTicketCost = ticket;
return this;
}
public HolidaysBuilder setBTransportCost(int transport) {
this.bTransportCost = transport;
return this;
}
public Holidays build() {
return new Holidays(bSportCost, bTicketCost, bTransportCost);
}
}

@Override
public String toString() {
return "Holidays {"+sportCost+", ticketCost"+ticketCost+", transportCost "+transportCost+"}";
}

}

Comme on peut voir, l'objet Holidays est créé avec une classe interne, HolidaysBuilder. C'est elle qui, à travers ses setters, permet l'exécution à la chaîne et construction de l'objet final grâce à la méthode build. Ce code devrait afficher sur l'écran:

Holidays are: Holidays {300, ticketCost200, transportCost 100}


Le Builder permet donc de bien contrôler le processus de création d'un objet complexe. Il gère souvent l'initialisation des objets beaucoup plus complexes que des vacances evoqués ci-dessus. Grâce à ce contrôle on peut librement manipuler la logique d'opérations - en rajouter une autre ou bien supprimer celle qui devient deprecated.
Bartosz KONIECZNY 08-09-2013 17:34 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 JavaScript

Comment accéder aux éléments du parent avec jQuery ?

jQuery, le célèbre framework JavaScript, permet d'écrire moins et de faire plus. On pourrait s'imaginer que cela s'applique uniquement aux opérations basiques, limitées au sein d'un seul document / d'une seule fenêtre. Cependant, avec jQuery on peut aussi manipuler les éléments d'une fenêtre-enfant. Ces opérations se limitent à la précision qu'il s'agit du document parent. Supposons qu'on ouvre une popup et qu'on veut récupérer les informations appartenant aux div avec l'identifiant "CONTENT" situé dans le document parent. Pour ce faire, il suffit de préciser qu'il s'agit du document supérieur :

$('#CONTENT', window.parent.document).html();