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
Un conseil Symfony2

Comment importer les routes ?

Parfois on a besoin de partager les routes en fonction de différents environnements (prod, dev / frontend, backend). Symfony2 gère ces cas très facilement. Pour pouvoir partager les routes il suffit tout simplement d'extraire les routes communes et de les importer par la suite. Dans notre exemple on a besoin de partager les routes qui définissent les requêtes AJAX. Pour ce faire, on va les sauvegarder dans le fichier routingAjax.yml, placé dans un bundle qui s'appelle RequestsAjaxBundle/Resource/config/routingAjax.yml. Alors, pour l'utiliser dans le fichier pour des routes de backend, il suffit de l'importer dans l'endroit voulu, comme ici :

# Ajax actions
ajaxRoutes:
    resource: "@RequestsAjaxBundle/Resources/config/routingAjax.yml"