Singleton : instance unique d'une classe
Par Francis Besset le mardi 19 janvier 2010 - Développements | Lien permanent

Vous débutez en Programmation Orientée Objet (POO) et vous souhaitez découvrir de nouveaux concepts ?
Dans cet article, je vais vous présenter la notion de Singleton en POO.
Les Singletons vous permettent d'instancier une seule et unique fois une classe. Le but principal est d'économiser des ressources.
Par exemple si dans votre application PHP, vous avez un fichier de configuration que vous chargez grâce à une classe, vous allez lire la totalité du fichier et enregistrer la configuration dans un attribut grâce au constructeur. Ensuite quand vous rechercherez une valeur d'un élément, vous utiliserez une méthode qui vous renverra le résultat.
Cependant en l'état rien ne vous empêche d'instancier une seconde fois votre classe et de relire une nouvelle fois le fichier de configuration pour le mettre en mémoire dans votre nouvelle instance.
Il est bien évident que vous aurez une perte de performances. C'est à ce moment qu'intervient la notion de Singleton.
Voilà à quoi ressemble actuellement votre classe de lecture de configuration :
<?php
class MaConfig {
private
$_config;
public function __construct()
{
if(is_readable('./inc/config.ini')) {
$this->_config = parse_ini_file('./inc/config.ini');
}
else {
throw new Exception('ini file "./inc/config.ini" does not exists.');
}
}
public function get($elem)
{
if(isset($this->_config[$elem])) {
return $this->_config[$elem];
}
return NULL;
}
public function set($elem, $value) {
$this->_config[$elem] = $value;
}
}
?>
Cette classe permet de lire un fichier de configuration de type INI. Une fois instanciée, cette classe stock dans l'attribut privée _config tous les éléments contenus dans le fichier.
Cependant avec le code ci-dessous vous irez droit dans la chute aux performances car vous instancierez plusieurs fois cette même classe :
<?php
$config = new MaConfig();
echo $config->get('login');
unset($config);
echo '<br />';
$config = new MaConfig();
echo $config->get('password');
?>
C'est vrai que ça l'air un peu bête comme ça de l'instancier deux fois de suite. Toutefois avec les inclusions d'autres scripts, vous avez vite fait d'en arriver à cette bêtise.
Heureusement le Singleton à fait son apparition en PHP5 grâce aux attributs privées et aux méthodes privées.
<?php
class MaConfig {
private
$_config;
private static
$_instance = NULL;
private function __construct()
{
if(is_readable('./inc/config.ini')) {
$this->_config = parse_ini_file('./inc/config.ini');
}
else {
throw new Exception('ini file "./inc/config.ini" does not exists.');
}
}
public static function getInstance()
{
if(is_null(self::$_instance)) {
self::$_instance = new MaConfig();
}
return self::$_instance;
}
public function __clone() {
throw new Exception('No clone! I am a Singleton.');
}
public function get($elem)
{
if(isset($this->_config[$elem])) {
return $this->_config[$elem];
}
return NULL;
}
public function set($elem, $value) {
$this->_config[$elem] = $value;
}
}
?>
Les différences que l'on peut noter entre notre première classe et celle-ci, sont :
- L'ajout d'un attribut privé statique nommé _instance.
- Le passage du constructeur en privé.
- L'ajout de la méthode statique getInstance.
- L'ajout de la méthode magique __clone. Dans l'exemple, la méthode __clone est publique. Lorsqu'elle sera appelé, elle lancera une exception. Vous pouvez tout aussi bien la mettre en privé.
Le fait de passer le constructeur en méthode privée nous permettra d'être sûr à 100% que ce devra être une méthode de cette classe qui devra l'instancier. Par ailleurs, c'est notre nouvelle méthode statique getInstance qui sera chargée de ce boulot. Elle enregistrera l'instance dans l'attribut privée statique _instance.
On a aussi ajouté la méthode magique __clone afin d'éviter de cloner cette classe puisqu'elle est Singleton.
Voici ce que ça donne en application :
<?php
$config = MaConfig::getInstance();
echo $config->get('login');
unset($config);
echo '<br />';
$config = MaConfig::getInstance();
echo $config->get('password');
?>
Dans le cas présent, notre classe n'a lu qu'une seule fois le fichier de configuration INI car notre méthode getInstance est pourvu d'une condition vérifiant si l'attribut _instance est de valeur NULL, auquel cas la classe sera instanciée et enregistré dans l'attribut privée statique _instance qui sera renvoyé à la fin du traitement de getInstance.
C'est donc des économies de ressources et un script plus rapide à l'exécution dans la poche.
C'est par ailleurs en écrivant cet article que j'ai découvert la méthode magique __clone qui permet de cloner une instance.



