Les designs patterns existent depuis bien plus longtemps que les systèmes d'informations. Ils ont été notamment utilisés dans le batiment.
Les designs patterns sont des motifs de conceptions de classes pour répondre et faciliter les problématiques que vous rencontrez dans vos développements
Il en existe de très nombreux, je n'aborderais ici que les principaux.
Des prérequis en programmation objet vous seront nécéssaires pour comprendre cet article, notamment les notions de :
- Classes, instances
- Extension de classe
- Interface
- Variables statiques
- Portée d'attributs
Les designs patterns qui vous sont présentés sont:
Présentation
Même si vous n'êtes pas familier avec les design pattern, vous avez peut être déjà entendu parler du design pattern singleton.
Ce design pattern permet d'obtenir une unicité lors de l'instanciation de la classe, de telle sorte que vous n'aurez pas plusieurs occurences d'une même classe alors que vous n'avez besoin que d'une seule.
Les exemples d'utilisation sont nombreux: classe de configuration, dao, etc...Dans cet article, j'ai choisi le cas d'une classe de configuration.
Haut de pageImplémentation du design pattern
La classe implémentant le singleton doit verrouiller l'appel au constructeur pour éviter que l'on puisse instancier plusieurs objets. Le constructeur a donc une portée privée.
Seulement pour obtenir notre instance (unique) de l'objet, la classe doit avoir une méthode statique qui se chargera de faire la construction et de renvoyer une référence sur l'objet si l'objet a déjà été crée.
Ensuite vous pouvez utiliser votre instance comme n'importe quel objet.Petite remarque concernant le modèle objet de PHP (5). Par défaut, il existe une méthode magique __clone qui permet de cloner n'importe quel objet. Dans nôtre cas, elle vas nous poser problème puisque nous ne souhaitons n'avoir qu'une seule instance. Nous allons surcharger cette méthode pour qu'elle renvoie une exception.
L'interface que nous utiliserons pour définir un singleton est la suivante:
<?php
/**
* $Id$
* @package Poo_Example
* @subpackage Singleton
*/
if (!interface_exists('Singleton_Interface')) {
/**
* Definit l'interface singleton
* @license http://opensource.org/licenses/gpl-license.php GNU Public License
* @package Poo_Example
* @subpackage Singleton
*/
interface Singleton_Interface
{
/**
* Retourne l'instance du singleton
* @access public
* @static
*/
public static function getInstance();
/**
* surcharge la methode clone pour lancer une exception
* @access public
*/
public function __clone();
}
}
Sans ce design pattern
Cet exemple très sommaire vous montre comment nous pourrions créer une classe de configuration sans utiliser le design pattern singleton; cette classe lit des valeurs de configuration à partir de fichiers ini.
La classe utilisée est la suivante:
<?php
/**
* $Id$
* @package Poo_Example
* @subpackage Singleton
*/
if (!class_exists('Singleton_Configuration_Without')) {
/**
* Classe de configuration qui charge des valeurs
* de configuration depuis un fichier ini dans le dossier courant
* @package Poo_Example
* @subpackage Singleton
*/
class Singleton_Configuration_Without
{
/**
* @var string Nom du fichier de configuration a charger
* @access private
*/
private $_configurationFile = 'singleton_config_ini.php';
/**
* @var array Valeurs de configurations chargees
* @access private
*/
private $_configurationsValues = array();
/**
* Constructeur
* @access public
* @return void
*/
public function __construct()
{
echo "dans le constructeur de la classe\n";
$file = dirname(__FILE__).'/'.$this->_configurationFile;
$this->_configurationsValues = parse_ini_file($file);
}
/**
* Retourne une valeur de configuration
* @access public
* @param string $configurationKey : la cle de configuration
* @return string
*/
public function getValue($configurationKey)
{
return $this->_configurationsValues[$configurationKey];
}
}
}
Nous l'éxécutons à l'aide du script suivant:
<?php
/**
* $Id$
* @package Poo_Example
* @subpackage Singleton
*/
/**
*
*/
define('__EXAMPLE_PATH__', realpath(dirname(__FILE__).'/../../'));
require_once __EXAMPLE_PATH__ . '/Autoload.php';
/**
* $configurationContent = "database_user = root\n".
* "database_password =\n".
* "database_host = localhost\n".
* "database_database = database\n";
* file_put_contents(__EXAMPLE_PATH__.'/config/singleton_config.ini', $configurationContent);
* @package Poo_Example
* @subpackage Singleton
*/
$config = new Singleton_Configuration_Without();
echo "valeur dans la configuration pour la cle database_host : [".
$config->getValue('database_host')."]\n";
$anotherConfig = new Singleton_Configuration_Without();
echo "valeur dans la configuration pour la cle for key database_user : [".
$config->getValue('database_user')."]\n";
echo "config est une instance de ConfigurationClass? [".
($config instanceof Singleton_Configuration_Without)."]\n";
echo "anotherConfig est une instance de ConfigurationClass? [".
($anotherConfig instanceof Singleton_Configuration_Without)."]\n";
echo "memes references? ".($config === $anotherConfig)."\n";
?>
A l'éxécution en ligne de commande, cela nous donne le résultat suivant:
valeur dans la configuration pour la cle database_host : [localhost]
dans le constructeur de la classe
valeur dans la configuration pour la cle for key database_user : [root]
config est une instance de ConfigurationClass? [1]
anotherConfig est une instance de ConfigurationClass? [1]
memes references?
Nous voyons donc que nous passons dans le constructeur à chaque fois que nous instancions la classe de configuration, et que les instances retournées sont bien distinctes.
Haut de pageAvec ce design pattern
L'implémentation de la classe de configuration avec le design pattern Singleton s'est faite de la façon suivante:
- Modification de la portée du constructeur pour qu'il ait une portée privée
- Définition de la méthode statique getInstance
- Surcharge de la méthode magique __clone
<?php
/**
* $Id$
* @package Poo_Example
* @subpackage Singleton
*/
if (!class_exists('Singleton_Configuration_With')) {
/**
*
*/
if (!defined('__EXAMPLE_PATH__')) {
define('__EXAMPLE_PATH__', realpath(dirname(__FILE__).'/../'));
}
require_once __EXAMPLE_PATH__ . '/Autoload.php';
/**
* Classe de configuration basee sur le singleton qui charge des valeurs
* de configuration depuis un fichier ini dans le dossier courant
* @package Poo_Example
* @subpackage Singleton
*/
class Singleton_Configuration_With implements Singleton_Interface
{
/**
* @var string Nom du fichier de configuration a charger
* @access private
*/
private $_configurationFile = 'singleton_config_ini.php';
/**
* @var array Valeurs de configurations chargees
* @access private
*/
private $_configurationsValues = array();
/**
* @var mixed l'instance du singleton
* @access private
* @static
*/
private static $_instance = null;
/**
* Retourne l'instance du singleton
* @access public
* @static
* @return mixed
*/
public static function getInstance()
{
if (is_null(self :: $_instance)) {
self :: $_instance = new Singleton_Configuration_With();
}
return self :: $_instance;
}
/**
* Constructeur
* @access private
* @return void
*/
private function __construct()
{
echo "dans le constructeur de la classe\n";
$file = dirname(__FILE__).'/'.$this->_configurationFile;
$this->_configurationsValues = parse_ini_file($file);
}
/**
* Retourne une valeur de configuration
* @access public
* @param string $configurationKey : la cle de configuration
* @return string
*/
public function getValue($configurationKey)
{
return $this->_configurationsValues[$configurationKey];
}
/**
* surcharge la methode clone pour lancer une exception
* @access public
* @return void
* @throws Exception
*/
public function __clone()
{
throw new Exception('vous ne pouvez cloner un objet singleton');
}
}
}
Le script d'éxécution à été légèrement modifié pour tenir compte de l'appel à la méthode statique getInstance:
<?php
/**
* $Id$
* @package Poo_Example
* @subpackage Singleton
*/
/**
*
*/
define('__EXAMPLE_PATH__', realpath(dirname(__FILE__).'/../../'));
require_once __EXAMPLE_PATH__ . '/Autoload.php';
/**
* $configurationContent = "database_user = root\n".
* "database_password =\n".
* "database_host = localhost\n".
* "database_database = database\n";
* file_put_contents(__EXAMPLE_PATH__.'/config/singleton_config.ini', $configurationContent);
* @package Poo_Example
* @subpackage Singleton
*/
$config = Singleton_Configuration_With :: getInstance();
echo "valeur dans la configuration pour la cle database_host : [".
$config->getValue('database_host')."]\n";
$anotherConfig = Singleton_Configuration_With :: getInstance();
echo "valeur dans la configuration pour la cle for key database_user : [".
$config->getValue('database_user')."]\n";
echo "config est une instance de ConfigurationSingletonClass? [".
($config instanceof Singleton_Configuration_With )."]\n";
echo "anotherConfig est une instance de ConfigurationSingletonClass? [".
($anotherConfig instanceof Singleton_Configuration_With )."]\n";
echo "memes references? ".($config === $anotherConfig)."\n";
?>
La sortie console de ce fichier d'exemple est la suivante:
valeur dans la configuration pour la cle database_host : [localhost]
valeur dans la configuration pour la cle for key database_user : [root]
config est une instance de ConfigurationSingletonClass? [1]
anotherConfig est une instance de ConfigurationSingletonClass? [1]
memes references? 1
Comme vous pouvez le voir, on ne passe qu'une seule fois dans le constructeur de la classe, quelque soit l'appel réalisé. Et à chaque fois, nous obtenons le même pointeur sur l'objet.
Haut de pageConclusion
De cette façon, vous assurez l'unicité de l'instance appelé. Vous serez ainsi sûrs que vous traitez bien avec le même objet.
Dans d'autres langages objets multi threads, il faut s'assurer dans le constructeur privé qu'un autre processus n'est pas en train d'instancier l'objet en posant un verrou. Dans le cas de PHP, ce problème ne se pose pas puisque PHP n'est pas multi thread.
Pour vous assurer de ne pas oublier d'implémenter certaines méthodes lors de la définition de votre singleton, il est préférable que lors de la définition de votre singleton vous implémentiez une interface qui définisse vos méthodes.
Haut de pageTélécharger les exemples
: 




