Creazione del modello di progettazione Singleton in PHP5


204

Come si potrebbe creare una classe Singleton usando le classi PHP5?



1
@Andrew Non istanziare quindi una seconda istanza che si collega al database. Passa quell'istanza dove è necessaria. La necessità di un Singleton è un odore di codice. Altro su gooh.posterous.com/singletons-in-php
Gordon,

3
@Andrew Mmmmkay. Senza offesa, ma ti suggerisco di procurarti un libro sulla qualità del software prima di continuare questa discussione. I singoli non semplificano ma complicano la normale manutenzione e sviluppo. In realtà, è il contrario: sono i test unitari che semplificano e consentono lo sviluppo in primo luogo.
Gordon,

3
@Andrew: supponi ora di aver bisogno di una sola connessione al database. Cosa succede quando cambiano le tue esigenze e devi effettivamente parlare con 2 server di database? Per non parlare del fatto che non puoi fidarti del tuo team per fare le cose nel modo giusto , creare un singleton non ti aiuterà affatto. Fai le cose fin dall'inizio e ottieni una squadra di cui ti puoi fidare e starai bene.
Ircmaxell,

4
Solo perché Singleton è stato abusato non lo rende un cattivo modello che dovrebbe essere evitato. Non odiare il Singleton. A volte è una soluzione perfettamente valida per un determinato problema. Meglio iniziare a discutere sul perché non dovremmo usarlo invece di provare solo emotivamente a degradarlo.
Gilles Lesire,

Risposte:


268
/**
 * Singleton class
 *
 */
final class UserFactory
{
    /**
     * Call this method to get singleton
     *
     * @return UserFactory
     */
    public static function Instance()
    {
        static $inst = null;
        if ($inst === null) {
            $inst = new UserFactory();
        }
        return $inst;
    }

    /**
     * Private ctor so nobody else can instantiate it
     *
     */
    private function __construct()
    {

    }
}

Usare:

$fact = UserFactory::Instance();
$fact2 = UserFactory::Instance();

$fact == $fact2;

Ma:

$fact = new UserFactory()

Genera un errore.

Vedi http://php.net/manual/en/language.variables.scope.php#language.variables.scope.static per comprendere gli ambiti delle variabili statiche e perché l'impostazione static $inst = null;funziona.


59
per confrontare le due istanze dovresti usare === piuttosto che ==. == restituirà true se $ fact1 e $ fact2 appartengono entrambi alla stessa classe, ma === restituisce true solo se sono entrambi la stessa istanza dello stesso oggetto.
Keith Twombley,

10
il metodo clone dovrebbe anche essere privato
Alex Petrov,

22
Questo metodo non ripristinerà l'istanza di UserFactory su null ogni volta che si chiama Instance ()? In java la variabile $ inst sarebbe un attributo statico privato che non dovrebbe essere reimpostato più volte, altrimenti potresti anche non renderlo un singleton.
Rudy Garcia,

8
Ecco una buona scrittura di perché e come dichiarare la variabile come statica nelle opere di funzione come l'autore intende: php.net/manual/en/...
hereswhatidid

10
Dovresti usare $ inst = new self (); non $ inst = new UserFactory (); per chiunque si imbatte in questo dopo. +1 per l'utilizzo di una metodologia PHP integrata.
Ligemer,

119

PHP 5.3 consente la creazione di una classe Singleton ereditabile tramite associazione statica tardiva:

class Singleton
{
    protected static $instance = null;

    protected function __construct()
    {
        //Thou shalt not construct that which is unconstructable!
    }

    protected function __clone()
    {
        //Me not like clones! Me smash clones!
    }

    public static function getInstance()
    {
        if (!isset(static::$instance)) {
            static::$instance = new static;
        }
        return static::$instance;
    }
}

Questo risolve il problema, che prima di PHP 5.3 qualsiasi classe che estendesse un Singleton avrebbe prodotto un'istanza della sua classe genitrice invece della sua.

Ora puoi fare:

class Foobar extends Singleton {};
$foo = Foobar::getInstance();

E $ foo sarà un'istanza di Foobar anziché un'istanza di Singleton.


1
Il binding statico tardivo è davvero un'ottima cosa in php 5.3. Peccato che non riesco ancora a usarlo.
Antonio CS

4
Da @ggsonic: "subclass should own its own static var. check this: echo get_class(Foobar::getInstance());echo get_class(Singleton::getInstance());".
Brock Adams,

4
Questo non funziona affatto, succede che Foobar è stata la prima classe che hai costruito?
Chris KL,

1
ancora la possibilità di clonare ..... "$ a = Singleton :: getInstance (); $ b = unserialize (serializza ($ a)); $ a! == $ b;"
Bortunac,

15
Questo non funziona quando c'è più di una sottoclasse! $instancerisiede in Singleton, non nella sottoclasse. Dopo aver istanziato alcune sottoclassi, getInstance () restituirà quell'istanza per tutte le sottoclassi.
mpartel,

116

Sfortunatamente la risposta di Inwdr si interrompe quando ci sono più sottoclassi.

Ecco una classe base Singleton ereditabile corretta.

class Singleton
{
    private static $instances = array();
    protected function __construct() {}
    protected function __clone() {}
    public function __wakeup()
    {
        throw new Exception("Cannot unserialize singleton");
    }

    public static function getInstance()
    {
        $cls = get_called_class(); // late-static-bound class name
        if (!isset(self::$instances[$cls])) {
            self::$instances[$cls] = new static;
        }
        return self::$instances[$cls];
    }
}

Codice di prova:

class Foo extends Singleton {}
class Bar extends Singleton {}

echo get_class(Foo::getInstance()) . "\n";
echo get_class(Bar::getInstance()) . "\n";

1
Questo è finora il più vicino alla corretta implementazione di Singleton. Dovresti anche considerare di lanciare il metodo __wakeup () per prevenire la non serializzazione.
Robert Rossmann,

In realtà devi lanciare un'eccezione o generare manualmente un errore: dichiarare la funzione protetta / privata genererà solo un E_WARNING che dice che non può accedere al metodo, ma che altrimenti continuerebbe.
Robert Rossmann,

Grazie. Normalmente ho tutti gli avvisi, ecc. Trasformati in eccezioni, quindi ho dimenticato la differenza quando ho testato: P
mpartel

Questa è l'unica soluzione che ho trovato che si occupa correttamente di più sottoclassi. Grazie!
Bob Dankert,

36

Il modo reale e moderno per creare il modello Singleton è:

<?php

/**
 * Singleton Pattern.
 * 
 * Modern implementation.
 */
class Singleton
{
    /**
     * Call this method to get singleton
     */
    public static function instance()
    {
      static $instance = false;
      if( $instance === false )
      {
        // Late static binding (PHP 5.3+)
        $instance = new static();
      }

      return $instance;
    }

    /**
     * Make constructor private, so nobody can call "new Class".
     */
    private function __construct() {}

    /**
     * Make clone magic method private, so nobody can clone instance.
     */
    private function __clone() {}

    /**
     * Make sleep magic method private, so nobody can serialize instance.
     */
    private function __sleep() {}

    /**
     * Make wakeup magic method private, so nobody can unserialize instance.
     */
    private function __wakeup() {}

}

Quindi ora puoi usarlo come.

<?php

/**
 * Database.
 *
 * Inherited from Singleton, so it's now got singleton behavior.
 */
class Database extends Singleton {

  protected $label;

  /**
   * Example of that singleton is working correctly.
   */
  public function setLabel($label)
  {
    $this->label = $label;
  }

  public function getLabel()
  {
    return $this->label;
  }

}

// create first instance
$database = Database::instance();
$database->setLabel('Abraham');
echo $database->getLabel() . PHP_EOL;

// now try to create other instance as well
$other_db = Database::instance();
echo $other_db->getLabel() . PHP_EOL; // Abraham

$other_db->setLabel('Priler');
echo $database->getLabel() . PHP_EOL; // Priler
echo $other_db->getLabel() . PHP_EOL; // Priler

Come vedi questa realizzazione è molto più flessibile.


4
Questa è la risposta più chiara sul modello Singleton all'interno di questo thread. Grazie.
Gus,

Ho implementato questo approccio e funziona come previsto: la seconda istanza diventa nulla. Tuttavia non avevo bisogno di estendere anche la classe concreta. Ho appena implementato Singleton :: instance () nel costruttore di quella classe concreta.
snaphuman

nella instancefunzione di $instancedovrebbe essere nullnonfalse
MIFAS

Sì, ma non è funzione, ma metodo.
Abraham Tugalov,

26

Probabilmente dovresti aggiungere un metodo __clone () privato per impedire la clonazione di un'istanza.

private function __clone() {}

Se non includi questo metodo, diventa possibile quanto segue

$inst1=UserFactory::Instance(); // to stick with the example provided above
$inst2=clone $inst1;

ora $inst1! == $inst2- non sono più la stessa istanza.


11
<?php
/**
 * Singleton patter in php
 **/
trait SingletonTrait {
   protected static $inst = null;

  /**
   * call this method to get instance
   **/
   public static function getInstance(){
      if (static::$inst === null){
         static::$inst = new static();
      }
      return static::$inst;
  }

  /**
   * protected to prevent clonning 
   **/
  protected function __clone(){
  }

  /**
   * protected so no one else can instance it 
   **/
  protected function __construct(){
  }
}

usare:

/**
 *  example of class definitions using SingletonTrait
 */
class DBFactory {
  /**
   * we are adding the trait here 
   **/
   use SingletonTrait;

  /**
   * This class will have a single db connection as an example
   **/
  protected $db;


 /**
  * as an example we will create a PDO connection
  **/
  protected function __construct(){
    $this->db = 
        new PDO('mysql:dbname=foodb;port=3305;host=127.0.0.1','foouser','foopass');
  }
}
class DBFactoryChild extends DBFactory {
  /**
   * we repeating the inst so that it will differentiate it
   * from UserFactory singleton
   **/
   protected static $inst = null;
}


/**
 * example of instanciating the classes
 */
$uf0 = DBFactoryChild::getInstance();
var_dump($uf0);
$uf1 = DBFactory::getInstance();
var_dump($uf1);
echo $uf0 === $uf1;

respose:

object(DBFactoryChild)#1 (0) {
}
object(DBFactory)#2 (0) {
}

Se stai usando PHP 5.4: il tratto è un'opzione, quindi non devi sprecare la gerarchia dell'ereditarietà per avere il modello Singleton

e nota anche che se usi tratti o estendi la classe di Singleton , un fine libero era quello di creare singleton di classi secondarie se non aggiungi la seguente riga di codice:

   protected static $inst = null;

nella classe del bambino

il risultato inaspettato sarà:

object(DBFactoryChild)#1 (0) {
}
object(DBFactoryChild)#1 (0) {
}

10
protected  static $_instance;

public static function getInstance()
{
    if(is_null(self::$_instance))
    {
        self::$_instance = new self();
    }
    return self::$_instance;
}

Questo codice può essere applicato a qualsiasi classe senza preoccuparsi del nome della sua classe.


8

Supporta più oggetti con 1 riga per classe:

Questo metodo imporrà i singleton su qualsiasi classe che desideri, inoltre devi solo aggiungere 1 metodo alla classe che desideri creare un singleton e questo lo farà per te.

Ciò memorizza anche gli oggetti in una classe "SingleTonBase" in modo da poter eseguire il debug di tutti gli oggetti che sono stati utilizzati nel sistema ricorrendo agli SingleTonBaseoggetti.


Crea un file chiamato SingletonBase.php e includilo nella radice del tuo script!

Il codice è

abstract class SingletonBase
{
    private static $storage = array();

    public static function Singleton($class)
    {
        if(in_array($class,self::$storage))
        {
            return self::$storage[$class];
        }
        return self::$storage[$class] = new $class();
    }
    public static function storage()
    {
       return self::$storage;
    }
}

Quindi, per qualsiasi classe che desideri creare un singleton, aggiungi questo piccolo metodo singolo.

public static function Singleton()
{
    return SingletonBase::Singleton(get_class());
}

Ecco un piccolo esempio:

include 'libraries/SingletonBase.resource.php';

class Database
{
    //Add that singleton function.
    public static function Singleton()
    {
        return SingletonBase::Singleton(get_class());
    }

    public function run()
    {
        echo 'running...';
    }
}

$Database = Database::Singleton();

$Database->run();

E puoi semplicemente aggiungere questa funzione singleton in qualsiasi classe tu abbia e creerà solo 1 istanza per classe.

NOTA: è sempre necessario rendere privato __construct per eliminare l'uso di new Class (); istanze.


5
class Database{

        //variable to hold db connection
        private $db;
        //note we used static variable,beacuse an instance cannot be used to refer this
        public static $instance;

        //note constructor is private so that classcannot be instantiated
        private function __construct(){
          //code connect to database  

         }     

         //to prevent loop hole in PHP so that the class cannot be cloned
        private function __clone() {}

        //used static function so that, this can be called from other classes
        public static function getInstance(){

            if( !(self::$instance instanceof self) ){
                self::$instance = new self();           
            }
             return self::$instance;
        }


        public function query($sql){
            //code to run the query
        }

    }


Access the method getInstance using
$db = Singleton::getInstance();
$db->query();

5

Non hai davvero bisogno di usare il modello Singleton perché è considerato un antipattern. Fondamentalmente ci sono molte ragioni per non implementare affatto questo modello. Leggi questo per iniziare con: Best practice sulle classi singleton PHP .

Se dopo tutto pensi ancora di dover usare il modello Singleton, allora potremmo scrivere una classe che ci permetterà di ottenere la funzionalità Singleton estendendo la nostra classe astratta SingletonClassVendor.

Questo è quello che mi è venuto in mente per risolvere questo problema.

<?php
namespace wl;


/**
 * @author DevWL
 * @dosc allows only one instance for each extending class.
 * it acts a litle bit as registry from the SingletonClassVendor abstract class point of view
 * but it provides a valid singleton behaviour for its children classes
 * Be aware, the singleton pattern is consider to be an anti-pattern
 * mostly because it can be hard to debug and it comes with some limitations.
 * In most cases you do not need to use singleton pattern
 * so take a longer moment to think about it before you use it.
 */
abstract class SingletonClassVendor
{
    /**
     *  holds an single instance of the child class
     *
     *  @var array of objects
     */
    protected static $instance = [];

    /**
     *  @desc provides a single slot to hold an instance interchanble between all child classes.
     *  @return object
     */
    public static final function getInstance(){
        $class = get_called_class(); // or get_class(new static());
        if(!isset(self::$instance[$class]) || !self::$instance[$class] instanceof $class){
            self::$instance[$class] = new static(); // create and instance of child class which extends Singleton super class
            echo "new ". $class . PHP_EOL; // remove this line after testing
            return  self::$instance[$class]; // remove this line after testing
        }
        echo "old ". $class . PHP_EOL; // remove this line after testing
        return static::$instance[$class];
    }

    /**
     * Make constructor abstract to force protected implementation of the __constructor() method, so that nobody can call directly "new Class()".
     */
    abstract protected function __construct();

    /**
     * Make clone magic method private, so nobody can clone instance.
     */
    private function __clone() {}

    /**
     * Make sleep magic method private, so nobody can serialize instance.
     */
    private function __sleep() {}

    /**
     * Make wakeup magic method private, so nobody can unserialize instance.
     */
    private function __wakeup() {}

}

Usa esempio:

/**
 * EXAMPLE
 */

/**
 *  @example 1 - Database class by extending SingletonClassVendor abstract class becomes fully functional singleton
 *  __constructor must be set to protected becaouse: 
 *   1 to allow instansiation from parent class 
 *   2 to prevent direct instanciation of object with "new" keword.
 *   3 to meet requierments of SingletonClassVendor abstract class
 */
class Database extends SingletonClassVendor
{
    public $type = "SomeClass";
    protected function __construct(){
        echo "DDDDDDDDD". PHP_EOL; // remove this line after testing
    }
}


/**
 *  @example 2 - Config ...
 */
class Config extends SingletonClassVendor
{
    public $name = "Config";
    protected function __construct(){
        echo "CCCCCCCCCC" . PHP_EOL; // remove this line after testing
    }
}

Giusto per dimostrare che funziona come previsto:

/**
 *  TESTING
 */
$bd1 = Database::getInstance(); // new
$bd2 = Database::getInstance(); // old
$bd3 = Config::getInstance(); // new
$bd4 = Config::getInstance(); // old
$bd5 = Config::getInstance(); // old
$bd6 = Database::getInstance(); // old
$bd7 = Database::getInstance(); // old
$bd8 = Config::getInstance(); // old

echo PHP_EOL."COMPARE ALL DATABASE INSTANCES".PHP_EOL;
var_dump($bd1);
echo '$bd1 === $bd2' . ($bd1 === $bd2)? ' TRUE' . PHP_EOL: ' FALSE' . PHP_EOL; // TRUE
echo '$bd2 === $bd6' . ($bd2 === $bd6)? ' TRUE' . PHP_EOL: ' FALSE' . PHP_EOL; // TRUE
echo '$bd6 === $bd7' . ($bd6 === $bd7)? ' TRUE' . PHP_EOL: ' FALSE' . PHP_EOL; // TRUE

echo PHP_EOL;

echo PHP_EOL."COMPARE ALL CONFIG INSTANCES". PHP_EOL;
var_dump($bd3);
echo '$bd3 === $bd4' . ($bd3 === $bd4)? ' TRUE' . PHP_EOL: ' FALSE' . PHP_EOL; // TRUE
echo '$bd4 === $bd5' . ($bd4 === $bd5)? ' TRUE' . PHP_EOL: ' FALSE' . PHP_EOL; // TRUE
echo '$bd5 === $bd8' . ($bd5 === $bd8)? ' TRUE' . PHP_EOL: ' FALSE' . PHP_EOL; // TRUE

Mentre leggevo le risposte più votate avevo in mente qualcosa del genere. Fortunatamente era già qui :)
hatef

3

Tutta questa complessità ("associazione statica tardiva" ... harumph) è, per me, semplicemente un segno del modello di classe / oggetto rotto di PHP. Se gli oggetti di classe fossero oggetti di prima classe (vedi Python), allora "$ _instance" sarebbe un'istanza di classe variabile di - un membro dell'oggetto di classe, al contrario di un membro / proprietà delle sue istanze, e anche al contrario di condiviso dai suoi discendenti. Nel mondo Smalltalk, questa è la differenza tra una "variabile di classe" e una "variabile di istanza di classe".

In PHP, mi sembra che dobbiamo prendere a cuore la guida secondo cui i pattern sono una guida per scrivere codice - potremmo forse pensare a un modello Singleton, ma provando a scrivere codice che eredita da una vera classe "Singleton" sembra fuorviato per PHP (anche se suppongo che un'anima intraprendente potrebbe creare una parola chiave SVN adatta).

Continuerò a codificare ogni singolo singleton separatamente, usando un modello condiviso.

Notate che sto assolutamente fuori dalla discussione sui singoli-sono-malvagi, la vita è troppo breve.


Le tue osservazioni sono giuste quando si visualizza la sempre crescente complessità del linguaggio PHP. Sembra che vengano aggiunte troppe nuove parole chiave per uscire da troppi buchi di progettazione diversi in troppi paradigmi di codifica diversi. Peggio ancora, a causa dell'elevato tasso di cambiamento e della versione distorta tra host e piattaforme di sviluppo, la "soluzione del giorno" di oggi (come i tratti nella risposta di @Eric Anderson [ stackoverflow.com/a/23998306/3696363] ), non funzionano sui sistemi di produzione che potrebbero eseguire una versione "stabile" anziché la "più recente, più grande".
Eliyahu Skoczylas,

2

So che questo probabilmente causerà una guerra di fiamma non necessaria, ma posso vedere come potresti desiderare più di una connessione al database, quindi concederei il punto che Singleton potrebbe non essere la soluzione migliore per questo ... tuttavia, ci sono altri usi del modello singleton che trovo estremamente utili.

Ecco un esempio: ho deciso di girare il mio MVC e il mio motore di templating perché volevo qualcosa di veramente leggero. Tuttavia, i dati che voglio visualizzare contengono molti caratteri matematici speciali come ≥ e μ e cosa hai ... I dati vengono archiviati come effettivi caratteri UTF-8 nel mio database anziché codificati in pre-HTML perché la mia app può fornire altri formati come PDF e CSV oltre all'HTML. Il luogo appropriato per la formattazione dell'HTML è all'interno del modello ("visualizza" se vuoi) che è responsabile del rendering di quella sezione di pagina (snippet). Voglio convertirli nelle loro entità HTML appropriate, ma la funzione get_html_translation_table () di PHP non è super veloce. Ha più senso recuperare i dati una volta e archiviarli come array, rendendoli disponibili per tutti. Qui' un campione ho bussato insieme per testare la velocità. Presumibilmente, questo funzionerebbe indipendentemente dal fatto che gli altri metodi che usi (dopo aver ottenuto l'istanza) fossero statici o meno.

class EncodeHTMLEntities {

    private static $instance = null;//stores the instance of self
    private $r = null;//array of chars elligalbe for replacement

    private function __clone(){
    }//disable cloning, no reason to clone

    private function __construct()
    {
        $allEntities = get_html_translation_table(HTML_ENTITIES, ENT_NOQUOTES);
        $specialEntities = get_html_translation_table(HTML_SPECIALCHARS, ENT_NOQUOTES);
        $this->r = array_diff($allEntities, $specialEntities);
    }

    public static function replace($string)
    {
        if(!(self::$instance instanceof self) ){
            self::$instance = new self();
        }
        return strtr($string, self::$instance->r);
    }
}
//test one million encodings of a string
$start = microtime(true);
for($x=0; $x<1000000; $x++){
    $dump = EncodeHTMLEntities::replace("Reference method for diagnosis of CDAD, but clinical usefulness limited due to extended turnaround time (≥96 hrs)");
}
$end = microtime(true);
echo "Run time: ".($end-$start)." seconds using singleton\n";
//now repeat the same without using singleton
$start = microtime(true);
for($x=0; $x<1000000; $x++){
    $allEntities = get_html_translation_table(HTML_ENTITIES, ENT_NOQUOTES);
    $specialEntities = get_html_translation_table(HTML_SPECIALCHARS, ENT_NOQUOTES);
    $r = array_diff($allEntities, $specialEntities);
    $dump = strtr("Reference method for diagnosis of CDAD, but clinical usefulness limited due to extended turnaround time (≥96 hrs)", $r);
}
$end = microtime(true);
echo "Run time: ".($end-$start)." seconds without using singleton";

Fondamentalmente, ho visto risultati tipici come questo:

php test.php
Tempo di esecuzione: 27.842966794968 secondi usando singleton
Durata: 237.78191494942 secondi senza usare singleton

Quindi, anche se non sono certamente un esperto, non vedo un modo più conveniente e affidabile per ridurre il sovraccarico delle chiamate lente per alcuni tipi di dati, rendendolo al contempo super semplice (singola riga di codice per fare ciò di cui hai bisogno). Concesso il mio esempio ha solo un metodo utile, e quindi non è meglio di una funzione definita a livello globale, ma non appena avrai due metodi, vorrai raggrupparli insieme, giusto? Sono lontano dalla base?

Inoltre, preferisco esempi che FANNO effettivamente qualcosa, poiché a volte è difficile visualizzare quando un esempio include istruzioni come "// fai qualcosa di utile qui" che vedo continuamente quando cerco tutorial.

Ad ogni modo, mi piacerebbe ricevere feedback o commenti sul perché usare un singleton per questo tipo di cose sia dannoso (o eccessivamente complicato).


1

Questo articolo tratta ampiamente l'argomento: http://www.phptherightway.com/pages/Design-Patterns.html#singleton

Nota quanto segue:

  • Il costruttore __construct()viene dichiarato in modo protectedda impedire la creazione di una nuova istanza al di fuori della classe tramite l' newoperatore.
  • Il metodo magico __clone()è dichiarato privateper impedire la clonazione di un'istanza della classe tramite l' cloneoperatore.
  • Il metodo magico __wakeup()è dichiarato privateper prevenire la serializzazione di un'istanza della classe tramite la funzione globale unserialize().
  • Una nuova istanza viene creata tramite associazione statica tardiva nel metodo di creazione statica getInstance()con la parola chiave static. Ciò consente la sottoclasse class Singletondell'esempio.

1

Ho scritto molto tempo fa pensato di condividere qui

class SingletonDesignPattern {

    //just for demo there will be only one instance
    private static $instanceCount =0;

    //create the private instance variable
    private static $myInstance=null;

    //make constructor private so no one create object using new Keyword
    private function  __construct(){}

    //no one clone the object
    private function  __clone(){}

    //avoid serialazation
    public function __wakeup(){}

    //ony one way to create  object
    public static  function  getInstance(){

        if(self::$myInstance==null){
            self::$myInstance=new SingletonDesignPattern();
            self::$instanceCount++;
        }
        return self::$myInstance;
    }

    public static function getInstanceCount(){
        return self::$instanceCount;
    }

}

//now lets play with singleton design pattern

$instance = SingletonDesignPattern::getInstance();
$instance = SingletonDesignPattern::getInstance();
$instance = SingletonDesignPattern::getInstance();
$instance = SingletonDesignPattern::getInstance();

echo "number of instances: ".SingletonDesignPattern::getInstanceCount();

0

Sono d'accordo con la prima risposta, ma dichiarerei anche la classe come finale in modo che non possa essere estesa in quanto l'estensione di un singleton viola il modello singleton. Inoltre, la variabile di istanza deve essere privata in modo che non sia possibile accedervi direttamente. Rendi anche il metodo __clone privato in modo da non poter clonare l'oggetto singleton.

Di seguito è riportato un esempio di codice.

/**
 * Singleton class
 *
 */
final class UserFactory
{
    private static $_instance = null;

    /**
     * Private constructor
     *
     */
    private function __construct() {}

    /**
     * Private clone method
     *
     */
     private function __clone() {}

    /**
     * Call this method to get singleton
     *
     * @return UserFactory
     */
    public static function getInstance()
    {
        if (self::$_instance === null) {
            self::$_instance = new UserFactory();
        }
        return self::$_instance;
    }
}

Esempio di utilizzo

$user_factory = UserFactory::getInstance();

Cosa ti impedisce di fare (che violerebbe il modello singleton ..

NON PUOI FARLO!

$user_factory = UserFactory::$_instance;

class SecondUserFactory extends UserFactory { }

0

Questo dovrebbe essere il modo giusto di Singleton.

class Singleton {

    private static $instance;
    private $count = 0;

    protected function __construct(){

    }

    public static function singleton(){

        if (!isset(self::$instance)) {

            self::$instance = new Singleton;

        }

        return self::$instance;

    }

    public function increment()
    {
        return $this->count++;
    }

    protected function __clone(){

    }

    protected function __wakeup(){

    }

} 

0

Mi è piaciuto il metodo @ jose-segura di usare i tratti ma non mi è piaciuta la necessità di definire una variabile statica nelle sottoclassi. Di seguito è una soluzione che la evita memorizzando nella cache le istanze in una variabile locale statica sul metodo factory indicizzato dal nome della classe:

<?php
trait Singleton {

  # Single point of entry for creating a new instance. For a given
  # class always returns the same instance.
  public static function instance(){
    static $instances = array();
    $class = get_called_class();
    if( !isset($instances[$class]) ) $instances[$class] = new $class();
    return $instances[$class];
  }

  # Kill traditional methods of creating new instances
  protected function __clone() {}
  protected function __construct() {}
}

L'utilizzo è lo stesso di @ jose-segura e non è necessaria la variabile statica nelle sottoclassi.


0

Classe di database che controlla se esiste un'istanza di database esistente che restituirà l'istanza precedente.

   class Database {  
        public static $instance;  
         public static function getInstance(){  
            if(!isset(Database::$instance) ) {  
                Database::$instance = new Database();  
            }  
           return Database::$instance;  
         }  
         private function __cunstruct() {  
           /* private and cant create multiple objects */  
         }  
         public function getQuery(){  
            return "Test Query Data";  
         }  
    }  
    $dbObj = Database::getInstance();  
    $dbObj2 = Database::getInstance();  
    var_dump($dbObj);  
    var_dump($dbObj2);  


/* 
After execution you will get following output: 

object(Database)[1] 
object(Database)[1] 

*/  

Rif http://www.phptechi.com/php-singleton-design-patterns-example.html


0

Questo è l'esempio di creare singleton nella classe Database

modelli di progettazione 1) singleton

class Database{
  public static $instance;
  public static function getInstance(){
    if(!isset(Database::$instance)){
    Database::$instance=new Database();

     return Database::$instance;
    }

  }

  $db=Database::getInstance();
  $db2=Database::getInstance();
  $db3=Database::getInstance();

  var_dump($db);
  var_dump($db2);
  var_dump($db3);

allora fuori put è -

  object(Database)[1]
  object(Database)[1]
  object(Database)[1]

usa solo una singola istanza e non crea 3 istanze


0

Esempio rapido:

final class Singleton
{
    private static $instance = null;

    private function __construct(){}

    private function __clone(){}

    private function __wakeup(){}

    public static function get_instance()
    {
        if ( static::$instance === null ) {
            static::$instance = new static();
        }
        return static::$instance;
    }
}

Spero d'aiuto.


-4

Ecco il mio esempio che offre la possibilità di chiamare come $ var = new Singleton () e anche la creazione di 3 variabili per verificare se crea un nuovo oggetto:

class Singleton{

    private static $data;

    function __construct(){
        if ($this::$data == null){
            $this->makeSingleton();
        }
        echo "<br/>".$this::$data;
    }

    private function makeSingleton(){
        $this::$data = rand(0, 100);
    }

    public function change($new_val){
        $this::$data = $new_val;
    }

    public function printme(){
        echo "<br/>".$this::$data;
    }

}


$a = new Singleton();
$b = new Singleton();
$c = new Singleton();

$a->change(-2);
$a->printme();
$b->printme();

$d = new Singleton();
$d->printme();

5
Solo che non è un singleton. È possibile creare più istanze della classe Singleton.
Andrew Moore,

Penso che sia dopo tutto, perché non importa quale istanza influisca sulla classe Singleton, le modifiche sono per tutte le istanze di Singleton. Ho aggiunto altre due funzioni sopra. Ora proviamo a modificare i dati in un'istanza e controlliamo gli altri. Quindi, non è Singleton e se no - cosa non è corretto?
bboydev,

5
Un singleton è una classe che consente solo un'istanza di se stessa. Creando più istanze, stai annullando quel principio.
Andrew Moore,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.