Le Design Pattern Flyweight (Poids mouche)
8 juin
Aujourd’hui je vous propose de parler Design Pattern, et plus particulièrement d’un pattern intéressant à mettre en place quand on cherche à alléger (en mémoire) une application : le pattern Flyweight (poid mouche).
Problème à résoudre
l’application doit manipuler de très nombreuses Entités (des produits pour une boutique par exemple), et chaque Entité est représentée par un Objet. L’instanciation de tous ces objets est très gourmande en mémoire :
$oDataMapper = new DataMapper_Product();
$tRowset = $oDataMapper->fetchAll($where);
foreach($tRowset as $oProduct) {
// one instance of Product $oProduct is created in each loop
echo $oProduct->getName();
}
On voit le problème : pour chaque ligne récupérée, un objet est instancié, donc est créé, chargé en mémoire… c’est lent et coûteux.
La solution : Flyweight
Il suffit de n’instancier qu’une seule fois l’objet en question, et de l’hydrater au fur et à mesure du besoin :
$oDataMapper = new DataMapper_Product();
$tRowset = $oDataMapper->fetchAll($where);
// Only one instance of Product is used
$oProduct = new Product;
foreach($tRowset as $tInfosAboutProduct) {
$oProduct->hydrate($tInfosAboutProduct);
echo $oProduct->getName();
}
Ce qui dans notre cas pourrait donner par exemple cette classe :
class Product extends Dao {
protected $_name;
protected $_price;
(... getter and setters ...)
public function hydrate(array $tData) {
$this
->setName($tData['name'])
->setPrice($tData['price']);
return $this;
}
}
Les performances sont dans ce cas meilleures, et l’utilisation mémoire est plus réduite.
Quand utiliser le pattern Flyweight ?
Ce type de pattern n’est pas forcément adapté dans toutes les situations, on l’utilise :
- quand on doit manipuler de très nombreux petits objets
- quand le coût (mémoire/vitesse) de cette manipulation est élevé
Et vous, avez-vous déjà utilisé ce pattern ? Quel est votre retour d’expérience ?


Personnellement, j’aurais plutôt travaillé sur $tRowset : le faire dériver de Iterator et faire en sorte qu’il charge dynamiquement les « Product »
En revanche, j’utilise souvent ce design pattern quand j’utilise des objet de Conversion (l’exemple qui me viens à l’esprit c’es Zend_Date : quand je veux convertir les dates au format ISO_8601 retourné par mon maper DAO vers un timestamp
Pour économiser de la mémoire (et des perfs), il faudrait aussi éviter d’implémenter des setter/getter quand ça n’a aucun sens (quand ils font juste lire/écrire dans une propriété quoi)
Là, dans ton exemple, ça ne sert à rien, ça le complexifie un poil, et ce n’est pas le but de ce billet que de montrer l’utilisation de setter/getter.
PHP != Java
Bonjour.
Attention, plusieurs petites imprécisions dans l’article.
Data_Mapper
————
La data_mapper (1) est censé retourner un « Product » prêt à l’emplois, et non une représentation sous la forme de tableau. Ainsi, l’hydratation est censée déjà être faite.
DAO
————
Product n’hérites pas de DAO. Le pattern DAO (Data Access Object) est censé lui même se charger de la récupération des données (en gros, une autre solution de lecture en base que le data mapper). Le DAO (2) contient des méthodes pour récupérer les données et retourne des objets métiers (tels Product par exemple)
Lazy Loading
————
Ici, tu cherches amha à démontrer l’utilitée du lazy loading plus que du Flyweight. Le lazy loading consistant à charger la donnée au moment ou l’on en a besoin. Le flyweight est un concept différent qui consiste à partager un même objet entre plusieurs instances.
@LaurentJ je suis globalement d’accord sur l’utilisation des getter/setter…. même si aujourd’hui j’y vois un intérêt pour les objets du modèle de domaine.
Bon….. je vais faire un sprint rédaction d’articles pour détailler tout ça !
1 – http://martinfowler.com/eaaCatalog/dataMapper.html
2 – http://java.sun.com/blueprints/corej2eepatterns/Patterns/DataAccessObject.html
Du coup j’ai crée un billet sur le poid mouche :
http://www.croes.org/gerald/blog/le-poid-mouche-flyweight-en-php/366/
Merci pour cet article, c’est bien expliqué