Design patterns dans Magento - prototype, object pool et iterator

applications internet utilitaires

Ce site ne sera plus alimenté de contenu après août 2014. Tous les nouveaux articles seront redigés pour www.waitingforcode.com
Le fossé entre le monde du PHP et celui d'autres langages existe encore bel et bien. Malgré d'avoir démontré dans le premier article sur design patterns en Magento, que même dans un langage parfois crade et souvent laxiste, on peut respecter de bonnes pratiques de codage.

Dans cette deuxième article on se focalisera sur 3 autres design patterns inclus dans Magento. Il présentera dans un premier temps le prototype. Il enchaînera ensuite avec object pool. Le dernier patron abordé sera iterator.

Design pattern : prototype
Ce patron de conception ressemble à un copier-coller d'un objet. Il a pour but de permettre aux développeurs de créer des objets à partir des ceux déjà existants. Il s'agit donc d'une initialisation par copie (ou plus précisément, par clonage pour rester politiquement correct).

Le prototype est très souvent associé à la présence des méthodes sémantiquement liées aux copies (copy, clone, imitate...) :

class User
{

private $login = "";

public function __construct($login)
{
$this->login = $login;
}

public function setLogin($login)
{
$this->login = $login;
}
public function getLogin()
{
return $this->login;
}

public function cloneUser()
{
// or use PHP's clone $object : http://php.net/manual/en/language.oop5.cloning.php
return $this;
}
}

$user = new User("user1");
echo $user->getLogin();
echo "

";
$otherUser = $user->cloneUser();
echo $otherUser->getLogin();
echo "

";
echo $otherUser->setLogin("user1 changed en otherUser");
echo $otherUser->getLogin();
echo "

";
$finalUser = clone $otherUser;
echo $finalUser->getLogin();
echo "

";


Magento utilise prototype dans le même objectif. Pour utiliser ce design pattern, il suffit de récupérer l'instance du modèle, comme par exemple ici : Mage:getModel('catalog/product')->getTypeInstance();

Design pattern : object pool
L'objectif de ce patron de conception est de mieux gérer la création des instances d'un objet. Grâce à ce contrôle, il aide également à optimiser les performances d'une application.

Une bonne illustration de l'object pool est l'exemple d'une librairie qui sera notre conteneur d'instances. Nous, on joue alors le rôle d'une application. Maintenant si on a besoin d'un livre (donc de l'instance d'un objet), on l'emprunte. Dès qu'on n'en a plus besoin, on remet le bouquin à sa place (donc libérons de l'espace dans notre application). Ce patron peut être très utile lors de la programmation concurrente dans autres langages que PHP. Cependant, pour garder une cohérence dans cette série d'articles, on va illustrer cet exemple par le code PHP. Regardons alors l’implémentation d'une librairie en PHP où un livre (une instance) peut être utilisé une fois :


class ObjectPool
{
protected static $instances = array();

public static function getInstance($instanceKey)
{
if(isset(self::$instances[$instanceKey]))
{
return null;
}
self::$instances[$instanceKey] = new $instanceKey;
return self::$instances[$instanceKey];
}

public static function releaseInstance($instanceKey)
{
// we allow only 1 instance per object (you can play with more ones and modify this method)
unset(self::$instances[$instanceKey]);
}

}

class ActionBooks
{
public function toString()
{
return "ActionBooks";
}
}


class DramaBooks
{
public function toString()
{
return "DramaBooks";
}
}
echo ObjectPool::getInstance("ActionBooks")->toString();
echo "
";
if(ObjectPool::getInstance("ActionBooks") !== null)
{
echo ObjectPool::getInstance("ActionBooks")->toString();
echo "
";
}
ObjectPool::releaseInstance("ActionBooks");
echo ObjectPool::getInstance("ActionBooks")->toString();
echo "
";
echo ObjectPool::getInstance("DramaBooks")->toString();
echo "
";


Magento utilise object pool dans le même objectif. La classe Varien_Object_Cache gère les instances déjà définies avec ses méthodes d'initialisation, de sauvegarde et de destruction.

Design pattern : iterator
L'itérateur est un des modèles de comportement dont l'objectif principal est de simplifier la manipulation des objets. Pour ceux qui sont familiers avec Java, il s'agira dans ce cas précis de l'interface Iterator du package java.util. Il sert à parcourir l'ensemble d'éléments d'une collection. Dans la vie quotidienne on peut le comparer à l'utilisation d'une télécommande quand on l'utilise pour zapper avec les boutons "plus" et "moins" (suivant et précédent).

Implémentons alors notre télécommande dans le code PHP avec l'utilisation de l'interface Iterator.


class Collection implements Iterator
{
protected $items = array();

protected $current = 0;

public function current()
{
return $this->items[$this->current];
}

public function key()
{
return $this->current;
}

public function next()
{
// do nothing : increment() handles the iteration
}

public function previous()
{
// do nothing
}

public function rewind()
{
$this->current = 0;
}

public function valid()
{
return (bool)(isset($this->items[$this->current]));
}

public function increment()
{
$this->current++;
}

public function putItems($items)
{
$this->items = $items;
}

}

$ch = new Collection;
$ch->putItems(array("TF1", "France 2", "France 3", "France 4", "ARTE", "M6", "W9"));
// you can make simplier, like here : http://php.net/manual/en/class.iterator.php#iterator.example.basic
while($ch->valid())
{
echo "Found {$ch->current()}
";
$ch->increment();
}


Dans Magento l'itérateur est utilisé pour manipuler les résultats retournés par les modèles. Tout cela est géré par la méthode getCollection() de la classe Mage_Core_Model_Abstract.

Ces design patterns pourraient s'illustrer encore mieux dans un environnement qui admet la programmation concurrente. Vous êtes d'ailleurs invités à approfondir ces concepts et de les implémenter dans un environnement plus exigeant que celui du PHP. Java paraît un endroit idéal pour effectuer ce saut. Et en attendant, on va encore essayer de masquer le fossé creusé par le passé.
Bartosz KONIECZNY 27-04-2012 17:11 applications web
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 valider les checkboxes avec Symfony2 ?

La validation des checkboxes sous Symfony2 se déroule avec une contrainte appelée ChoiceConstraint.

Voici l'exemple de l'utilisation:

$metadata->addPropertyConstraint('orderPreferedGift', new Choice(array('choices' => Gifts::getGiftTypes(true), 'multiple' => true, 'min' => 1,  'multipleMessage' => "Veuillez choisir au moins un type de cadeau", 'groups' => array('validationGroup'))));


Cette contrainte est très puissante. On peut déterminer par exemple la quantité des champs minimale ou maximale à cocher par utilisateur. Il est également possible de vérifier le types des valeurs.