Le pattern Singleton est trés souvent une bonne alternative à l'utilisation de classes possédant uniquement des méthodes statiques. Il permet de n'instancier qu'une seule fois une classe, et d'accéder toujours à cette même instance au moyen d'une méthode statique. Une brillante explication a déjà été faite par petepx dans son ticket DESIGN PATTERNS #1.

Sur le même principe, lors de la création de mon moteur de blog, je voulais économiser le plus possible de requêtes SQL, et donc éviter d'instancier deux fois le même objet. Par exemple, pour un ticket, il peut appraître trois fois sur la page d'accueil, dans des templates (et donc des scripts PHP) différents :

  • Dans le menu "Dernières entrées"
  • Dans le menu "Derniers commentaires"
  • Dans le contenu principal
Il s'agissait donc d'étendre le pattern Singleton pour qu'il me retourne à chaque fois la même instance, remplie depuis la base de données, mais l'instance que je veux.

Comme tout enregistrement d'une base de données bien concue, chaque ticket possède évidemment un identifiant unique, qui va alors me servir à distinguer le ticket que je veux en transmettant cet identifiant à ma méthode statique. Cet identifiant va également servir d'indice pour une propriété de type tableau stockant toutes les instances déjà créées.

Voici comment cela se présente en PHP4, qui je vous le rappelle n'offre pas la possibilité de faire des propriétés statiques. Il faut donc contourner cette limitation par l'utilisation de variables globales :// Déclaration de la classe
class Ticket {
   // Identifiant du ticket
   var $_id;
   // Fonction constructeur
   // Accés "private"
   function Ticket($id = 0) {
      if ($id > 0) {
         $this->_id = (int)$id;
      }
   }
   // Methode getInstance
   // Retourne une référence vers l'instance
   // correspondant à l'identifiant
   function &getInstance($id) {
      // Si l'indice n'est pas numérique, ne retourne rien
      if (!is_numeric($id)) {
         return;
      }
      // Teste l'existance de la propriété "statique"
      if (!isset($GLOBALS["INSTANCES_TICKETS"])) {
         $GLOBALS["INSTANCES_TICKETS"] = Array();
      }
      // Teste si l'instance demandée est déjà créée
      // Si ce n'est pas le cas, la classe est instanciée
      if (!isset($GLOBALS["INSTANCES_TICKETS"][(int)$id])) {
         $GLOBALS["INSTANCES_TICKETS"][(int)$id] = new Ticket($id);
      }
      // Retourne l'instance
      return $GLOBALS["INSTANCES_TICKETS"][(int)$id]
   }
}
// Utilisation
$monTicket =& Ticket::getInstance(124);
Voici ce que cela donnerait en AS2 : class Ticket {
   // Identifiant du ticket
   private var _id:Number;
   // Tickets instanciés
   private static var _lstInstances:Array = new Array();
   // Fonction constructeur
   private function Ticket($id:Number) {
      if ($id != undefined) {
         this._id = Number($id);
      }
   }
   // Méthode statique getInstance
   public static function getInstance($id:Number):Ticket {
      if ($id == undefined) {
         return;
      }
      // Si l'instance demandée n'existe pas, on la crée
      if (_lstInstances[$id] == undefined) {
         _lstInstances[$id] = new Ticket($id);
      }
      // On retourne l'instance
      return _lstInstances[$id];
   }
}
// Utilisation
var monTicket = Ticket.getInstance(124);

Suite à cela, j'accède aux propriétés de mon ticket via des getters, qui vérifient auparavant si l'instance à bien été remplie depuis la base de données (ou depuis un XML, ou depuis un requete serveur dans le cas de Flash). Ainsi, au premier accés, les propriétés de l'objet sont renseignées, et les accés suivants se font toujours sur la même instance ! 8)