Faire simple, c'est compliqué. Mais c'est mon métier.
Vieil article à reprendre car incomplet. L'objectif est de présenter le patron de conception "Singleton" mais de nos jours, on privilégie l'utilisation de conteneurs de services plutôt que l'usage d'un singleton.
J'ai fait la connaissance des design patterns et plus particulièrement le Singleton lors de mon deuxième job d'été à l'INRA en 2006, alors que je n'étais qu'un petit étudiant de l'IUT.
A l'époque, on m'avait demandé de réaliser un système d'export de base de
données MySQL en PHP. En ce temps là (et je crois que cela n'a pas beaucoup
changé) la littérature sur le sujet nous proposait d'effectuer des requetes à
grand coup de mysql_connect
, de mysql_query
et de mysql_fetch_array
.
Or s'il y a bien une chose que l'on nous enseignait en cours, c'était de miser un maximum sur la généricité. Et il faut bien avouer que la dizaine de lignes nécessaire à chaque requête fait toujours mal aux yeux quand on lit le code. J'ai donc naturellement créé une classe d'accès aux données (mon premier contact avec une couche DAO) en y ajoutant une méthode publique pour chaque requête dont j'avais besoin (ce qui n'est pas bien, mais on y reviendra dans un prochain post)
Le soucis c'est que parfois, une transaction nécessite plusieurs requêtes.
J'avais donc remanié ma classe pour qu'elle mette en attente celles-ci jusqu'à
ce que je lui demande d'envoyer la sauce à l'aide d'un élégant $database->commit();
Le problème auquel je n'avais pas pensé, c'était "comment garder une référence sur cet objet quand on y accède d'un peu partout?".
On peut espérer que si je fais $db = new Database();
alors il suffit de ne
jamais réinitialiser $db
et on pourra faire $db->commit();
n'importe
où, n'importe quand. Ca, c'est quand on n'a pas suivi le cours sur la portée
des variables.
Car si j'initalise $db
dans une fonction A()
, elle ne sera pas
accessible depuis la fonction B()
.
Oui, c'est vrai... on peut définir $db
à l'aide de l'instruction global $db = Database();
Si je fais ça dans la fonction A()
, $db
sera toujours définie dans
la fonction B()
. Ca, c'est la solution de ceux qui ont suivi le
cours sur la portée des variables mais pas celui sur la sécurité.
Enfin une bonne solution! Fiable et efficace... je définie une fonction fn1($db)
et une fonction fn2($db)
, j'initialise la variable $db
à l'extérieur et je la passe à fn1
ou fn2
lorsque je les appelle.
$db = new Database();
fn1($db);
fn2($db);
$db->commit();
Une solution pas si mauvaise que ça, si l'on est sûr que fn1
ou fn2
auront besoin d'une requête. Si ce n'est pas le cas, on aura initialisé $db
pour rien...
Voici un exemple:
class Database
{
// instance de la classe
private static $instance;
// Un constructeur privé ; empêche la création directe d'objet
private function __construct()
{
echo 'Je suis construit';
}
// La méthode singleton
public static function singleton()
{
if (!isset(self::$instance)) {
$c = __CLASS__;
self::$instance = new $c;
}
return self::$instance;
}
}
Il devient alors possible de faire appel à la même instance depuis n'importe où dans notre code:
fn1(); // dans fn1, on appelle Database::singleton()
fn2(); // dans fn2, on appelle Database::singleton()
$db = Database::singleton(); // ici aussi, on appelle Database::singleton()
$db->commit();
Attention. Cette façon de faire est démodée mais c'est ainsi que l'on utilise un singleton
J'y parle beaucoup technique de développement web, logiciels libres, et autres.
Ce site est entièrement consultable sans cookies et sans Javascript