Flyweight

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
Certaines situations dans le développement nécessitent des économies à tout prix. Il peut s'agir aussi bien des économies visant à lancer plus rapidement une action que celles dont le but est de minimaliser l'utilisation des ressources. Un design pattern a été défini pour toutes ces situations. Il s'appelle flyweight.

Ce patron de conception, flyweight (poids mouche), facilite la réutilisation des objets gourmands pendant l'initialisation. Cette réutilisation s'explique à travers l'identification d'un état shareable (partageable) et non-sharable (non-partageable). Car malgré les caractéristiques matériel plus que raisonnables dans le contexte des applications modernes, la création des objets à l'infini peut s’avérer fatale à long terme aussi bien pour la performance que pour la stabilité des systèmes.

Le poids mouche est donc une solution parfaite dans les deux cas suivants :
- la manipulation d'un très grand nombre de petits objets
- la création des objets est coûteuse en mémoire ou en vitesse

Un bon exemple de flyweight est un dessin. Ce dessin est composé de lignes et couleurs. Une ligne contient deux caractéristiques : la longueur et l'orientation (horizontale/verticale). Maintenant si on veut dessiner le mur d'une maison, on prendra deux lignes de la même longueur et de la même orientation. Au lieu de créer deux objets, on créera juste un seul et on réutilisera l'autre. Pareil pour les couleurs. Si l'on souhaitera peindre les arbres et l'herbe en vert, on utilisera la même instance de la classe Couleur dont la caractéristique valeur correspond au vert.

A travers ce descriptif on peut distinguer les acteurs suivants :
- flyweight (objet au poids mouche) : définition de l'interface des objets du poids mouche.
- concrete flyweight (l'implémentation concrète de l'objet poids mouche) : l'implémentation concrète de l'objet du poids mouche.
- flyweight factory (la fabrique de l'objet poids mouche) : l'endroit qui se charge de gérer les objets poids mouche. Si un objet n'existe pas, c'est la fabrique que l'initialise. Dans le cas contraire, c'est elle qui retourne l'instance déjà créée et correspondante à la demande.
- client : l'instance qui récupère les objets du poids mouche.

Il est important de penser à l'identité des objets. Si l'on crée des objets dont l'identité est importante, comme par exemples les objets représentant des personnes identifiées par leurs noms et prénoms, ce ne serait pas judicieux de les partager entre différents clients. Pareil, si le nombre d'objets censés d'être partagés est petite, ce serait peut-être plus économique de créer plusieurs objets mais non-partageables.

Exemple du flyweight
Dans notre exemple du code on présentera le partage des stylos dans un bureau. Supposons qu'on a un stylo de chaque couleur : rouge, noir, bleu, vert et blanc. L'entreprise dans laquelle travaillent ces personnes n'a pas d'argent pour en commander plusieurs exemplaires. C'est la raison pour laquelle chaque employé doit emprunter un stylo. Comme on peut déjà supposer, le client sera bien l'employé. Le flyweight, c'est-à-dire l'interface le définissant, sera bien un stylo. Le cas concret flyweight sera le stylo de chaque couleur. La fabrique sera la personne qui détiendra tous ces stylos et les redistribuera à des clients. Attention. Dans notre exemple on ne s'occupe pas de la synchronisation éventuelle du partage dans les environnements multi-threading. Ceci pour faciliter la lecture.


class People{
private PencilFactory pencilFactory;

public void borrow(final String[] colors){
for(final String c : colors){
System.out.println("Intended color : "+c);
System.out.println("Received pencil instance : "+this.pencilFactory.borrowPencil(c));
}
}

public void setPencilFactory(final PencilFactory pencilFactory){
this.pencilFactory=pencilFactory;
}
}

On commence par le demandeur. L'employé connaît l'existence d'une fabrique (gestionnaire des stylos). Sa méthode borrow() va se charger de demander auprès du gestionnaire le stylo de chaque couleur.


interface WrittingObject {
}

class Pencil implements WrittingObject {
private final String color;
private final int id;

public Pencil(final String color){
this.color=color;
this.id=new Random().nextInt();
}

@Override
public String toString(){
return "Pencil's instance for color : "+this.color+", id "+this.id;
}
}

Ci-dessus les codes des objets flyweight. L'interface définit le concept tandis que la classe Pencil le cas concret. L'attribut id, qu'on va lire dans la méthode toString(), nous servira à prouver qu'une seule instance est utilisée.


class PencilFactory{
private final Map<String, Pencil> pencils=new HashMap<String, Pencil>();

public Pencil borrowPencil(final String color){
if(!this.pencils.containsKey(color)){
this.pencils.put(color, new Pencil(color));
}
return this.pencils.get(color);
}

public int getPencilsSize(){
return this.pencils.size();
}
}

Là on voit la fabrique. Elle contient une liste des stylos déjà créés. On peut dire que ces stylos-là correspondent à des stylos déjà commandés par la société et déjà mis entre les mains du gestionnaire. On voit que si un stylo n'existe pas, il est tout de suite "commandé" (initialisation new Pencil(color)).


final PencilFactory pencilFabctory=new PencilFactory();
final People sylvester=new People();
sylvester.setPencilFactory(pencilFabctory);
final People anne=new People();
anne.setPencilFactory(pencilFabctory);
System.out.println("Sylvester's pencils :");
sylvester.borrow(new String[] {"black", "white", "green", "blue", "blue", "black", "green", "white", "white", "black"});
System.out.println("");
System.out.println("Anne's pencils :");
anne.borrow(new String[] {"black", "white", "red"});

Ce code présente l'utilisation de l'objet poids mouche. Le code ne contient rien de compliqué à comprendre.


Sylvester's pencils :
Intended color : black
Received pencil instance : Pencil's instance for color : black, id -565839267
Intended color : white
Received pencil instance : Pencil's instance for color : white, id 2014564860
Intended color : green
Received pencil instance : Pencil's instance for color : green, id -43949025
Intended color : blue
Received pencil instance : Pencil's instance for color : blue, id -899737848
Intended color : blue
Received pencil instance : Pencil's instance for color : blue, id -899737848
Intended color : black
Received pencil instance : Pencil's instance for color : black, id -565839267
Intended color : green
Received pencil instance : Pencil's instance for color : green, id -43949025
Intended color : white
Received pencil instance : Pencil's instance for color : white, id 2014564860
Intended color : white
Received pencil instance : Pencil's instance for color : white, id 2014564860
Intended color : black
Received pencil instance : Pencil's instance for color : black, id -565839267

Anne's pencils :
Intended color : black
Received pencil instance : Pencil's instance for color : black, id -565839267
Intended color : white
Received pencil instance : Pencil's instance for color : white, id 2014564860
Intended color : red
Received pencil instance : Pencil's instance for color : red, id -1259127258

La sortie, et plus précisément le paramètre id, nous prouve bien qu'on réutilise à chaque fois le même objet.


Flyweight est donc un patron de conception qui facilite le partage d'un objet entre plusieurs demandeurs. Il permet d'optimiser les coûts de création, mais aussi d'économiser la place de stockage. Mais avant de définir si un objet peut être flyweight, il faut s'assurer si l'identité de chaque objet n'est pas importante.
Bartosz KONIECZNY 27-11-2013 06:12 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 Symfony2

Comment exécuter son code dans le listener ?

Pour ce faire il suffit de surcharger la méthode handle(Request $request, $type = self::MASTER_REQUEST, $catch = true) qui se trouve dans AppKernel.php .

Une nouvelle méthode handle() peut se présenter ainsi :

function handle(Request $request, $type = self::MASTER_REQUEST, $catch = true)
{
  echo 'handle request';

  return parent::handle($request, $type, $catch);
}