// Singleton de connexion à la base de données
class Database
{
protected static $instance;
protected function __construct() {}
protected function __clone() {}
public function getInstance()
{
if( is_null(static::$instance) )
{
static::$instance = new static();
}
return static::$instance;
}
}
Pour moi, un singleton est mal utilisé quand je vois ce code :
class Foobar
{
public function doStuff()
{
Database::getInstance()->query($query);
}
}
Ici, le singleton n'est utiliser que pour accéder facilement à la connexion, et pose de gros soucis de couplage et de maintenance.class Foobar
{
protected $conn;
public function __construct(Database $conn)
{
$this->conn;
}
public function doStuff()
{
$query = 'Something interessting';
$this->conn->query($query);
}
}
$foobar = new Foobar(Database::getInstance());
Dans cet exemple, la classe Foobar gère sa connexion sans savoir d'où elle vient.
tout à fait d'accord, le singleton n'est qu'une manière propre et standardisé d'écrire qu'une Class ne sera instanciée qu'une seul fois, tout dépend de l’envergure de ton projet mais pour un projet à long terme je te conseil d'utiliser le singleton afin de faciliter la compréhension du code au futures informaticiens.Les deux se valent, c'est juste qu'on préfère avoir un objet à manipuler qu'un appel statique.
Bref c'est question de goût et de mode. ^^
De plus avec une class static tu ne pourras pas faire de multiton, d'où l'utilisation d'un objet instanciable pour le singleton pour garder en cohérence d'évolutivité.
Cette phrase montre que tu as mal compris l'intérêt du singleton.Par exemple, pour Log::getInstance->addTrace(), je suis plus tenté d'utiliser directement quelque chose comme trace::add() en ayant un object qui ne fait que des traces dans les log. J'ai du mal à imaginer ce qui pourrait changer dans une tel fonction.
La différence tiens en la maintenabilité de ton code. S'il est gavé de déclaration de classe externe, ce code n'est pas indépendant de cette classe externe.Pour ce qui est de l'injection de dépendance, ca reste mystérieux. Je récupère des objets pour utiliser des fonctions, tant que la fonction existe, il n'y a pas d'impact sur le code extérieur à mon object. Que j'utilise une fonction pour récupérer l'objet externe ou une fonction pour envoyer l'objet, j'ai du mal à voir ce que ca change. Pour moi dans un cas, mon objet fait des getinstance() et dans l'autre il faut que je fasse des set après chaque création.
<?php
class Foo {
protected $logger;
public function __construct()
{
$this->logger = new Logger(); // Si je veux déplacer la classe Foo, il faut que j'emporte la classe Logger
}
}
<?php
class Foo {
protected $logger;
public function __construct()
{
$this->logger = new Logger(); // Si je veux déplacer la classe Foo, il faut que j'emporte la classe Logger.
// De plus, si je veux remplacer ma classe Logger par autre chose, ça représente beaucoup de travail
}
public function setLogger(InterfaceLogger $logger)
{
$this->logger = $logger; // Ici, je n'ai que l'interface à emporter, et si je compte changer ma classe de Log, à partir du moment où elle implémente l'interface, c'est ok
}
}
Est-ce plus clair maintenant ?
<?php
class Foo {
protected $logger;
public function __construct()
{
$this->logger = log::getinstance(); // Je suis coincer, je récupère un objet de type fixé par l'objet log. Ca veux dire que j'ai un objet log par projet ou un objet foo par projet (OK, je comprends le problème)
}
public function setLogger(InterfaceLogger $logger)
{
$this->logger = $logger; // J'avoue ne utiliser utiliser les interfaces en php... et en même temps regretter que php ne soit pas plus typé.
}
}
Je crois que j'ai compris le problème que résout cette solution. Je n'y suis pas confronter car je ne garde pas le source de mes projets. Enfin je garde des sources que j'adapte à chaque fois. Une fois le projet terminé, j'oublie tout et si je dois intervenir à nouveau (jamais ?), on me rend les sources
<?php
class Foo {
protected $logger;
public function __construct()
{
}
public function setLogger(InterfaceLogger $logger)
{
$this->logger = $logger; // Ici, je n'ai que l'interface à emporter, et si je compte changer ma classe de Log, à partir du moment où elle implémente l'interface, c'est ok
}
}