Tworzenie wzorca projektowego Singleton w PHP5

204

Jak stworzyć klasę Singleton za pomocą klas PHP5?

Andrew Moore
źródło
1
@Andrew Nie należy wtedy tworzyć drugiej instancji łączącej się z bazą danych. Przekaż tę instancję tam, gdzie jest potrzebna. Potrzeba Singletona to Zapach Kodowy. Więcej na gooh.posterous.com/singletons-in-php
Gordon
3
@Andrew Mmmmkay. Bez urazy, ale sugeruję, abyś dostał książkę o jakości oprogramowania, zanim będziemy kontynuować dyskusję. Singletony nie upraszczają, ale utrudniają normalną konserwację i rozwój. W rzeczywistości jest odwrotnie: testy jednostkowe upraszczają i umożliwiają rozwój.
Gordon
3
@Andrew: Zakładasz teraz, że potrzebujesz tylko jednego połączenia z bazą danych. Co się stanie, gdy zmienią się twoje wymagania i rzeczywiście potrzebujesz porozmawiać z 2 serwerami baz danych? Nie wspominając o tym, jeśli nie możesz ufać swojemu zespołowi, że zrobi wszystko dobrze , utworzenie singletonu nie pomoże ci w najmniejszym stopniu. Rób rzeczy od samego początku i zdobądź zespół, któremu możesz zaufać, a wszystko będzie dobrze.
ircmaxell,
4
Tylko dlatego, że Singleton został nadużyty, nie czyni go złym wzorcem, którego należy unikać. Nie nienawidzę Singleton. Czasami jest to doskonale dobre rozwiązanie określonego problemu. Lepiej zacznij argumentować, dlaczego nie powinniśmy go używać zamiast emocjonalnie próbować go zniszczyć.
Gilles Lesire,

Odpowiedzi:

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()
    {

    }
}

Używać:

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

$fact == $fact2;

Ale:

$fact = new UserFactory()

Zgłasza błąd.

Zobacz http://php.net/manual/en/language.variables.scope.php#language.variables.scope.static, aby zrozumieć zakresy zmiennych statycznych i dlaczego ustawienie static $inst = null;działa.

Arount
źródło
59
aby porównać dwa wystąpienia, powinieneś użyć === zamiast ==. == zwróci prawdę, jeśli $ fact1 i $ fact2 są tej samej klasy, ale === zwróci prawdę tylko wtedy, gdy oba będą tą samą instancją tego samego obiektu.
Keith Twombley,
10
metoda klonowania powinna być również prywatna
Alex Petrov
22
Czy ta metoda nie zresetuje instancji UserFactory do wartości null za każdym razem, gdy wywołasz Instance ()? W java zmienna $ inst byłaby prywatnym atrybutem statycznym, którego nie należy resetować w kółko, w przeciwnym razie równie dobrze możesz nie uczynić go singletonem.
Rudy Garcia
8
Oto dobry opis tego, dlaczego i jak deklarowanie zmiennej jako funkcji statycznej działa zgodnie z zamierzeniami
hereswhatidid
10
Powinieneś używać $ inst = new self (); not $ inst = new UserFactory (); dla każdego, kto natknie się na to później. +1 za użycie wbudowanej metodologii PHP.
Ligemer,
119

PHP 5.3 pozwala na tworzenie dziedzicznej klasy Singleton poprzez późne wiązanie statyczne:

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;
    }
}

To rozwiązuje problem polegający na tym, że przed PHP 5.3 każda klasa, która rozszerzyła Singleton, produkowałaby instancję swojej klasy nadrzędnej zamiast własnej.

Teraz możesz zrobić:

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

A $ foo będzie instancją Foobar zamiast instancji Singleton.

selfawaresoup
źródło
1
Późne wiązanie statyczne jest rzeczywiście bardzo dobrą rzeczą w php 5.3. Szkoda, że ​​nadal nie mogę z niego korzystać.
AntonioCS,
4
Od @ggsonic: "subclass should own its own static var. check this: echo get_class(Foobar::getInstance());echo get_class(Singleton::getInstance());".
Brock Adams,
4
To w ogóle nie działa, tak się składa, że ​​Foobar była pierwszą klasą, którą zbudowałeś?
Chris KL
1
wciąż istnieje możliwość klonowania ..... "$ a = Singleton :: getInstance (); $ b = unserialize (serialize ($ a)); $ a! == $ b;"
bortunac
15
To nie działa, gdy istnieje więcej niż jedna podklasa! $instancerezyduje w Singleton, a nie w podklasie. Po utworzeniu instancji jakiejś podklasy funkcja getInstance () zwróci tę instancję dla wszystkich podklas.
mpartel
116

Niestety odpowiedź Inwdr nie działa, gdy istnieje wiele podklas.

Oto poprawna dziedziczna klasa podstawowa Singleton.

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];
    }
}

Kod testowy:

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

echo get_class(Foo::getInstance()) . "\n";
echo get_class(Bar::getInstance()) . "\n";
mpartel
źródło
1
Jest to jak dotąd najbliższa poprawna implementacja Singleton. Powinieneś również rozważyć wprowadzenie metody __wakeup () , aby zapobiec odseseriowaniu.
Robert Rossmann
W rzeczywistości musisz albo zgłosić wyjątek, albo zgłosić błąd ręcznie - zadeklarowanie funkcji jako chronionej / prywatnej spowoduje tylko wyświetlenie E_WARNING, mówiącego, że nie może uzyskać dostępu do metody, ale w przeciwnym razie będzie kontynuowane.
Robert Rossmann
Dzięki. Zwykle wszystkie ostrzeżenia itp.
Zmieniły
To jedyne rozwiązanie, które znalazłem, i które właściwie radzi sobie z wieloma podklasami. Dziękuję Ci!
Bob Dankert
36

Prawdziwy i nowoczesny sposób na stworzenie Singleton Pattern to:

<?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() {}

}

Więc teraz możesz go używać jak.

<?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

Jak widzisz, ta realizacja jest znacznie bardziej elastyczna.

Abraham Tugałow
źródło
4
To jest najwyraźniejsza odpowiedź na temat wzoru Singleton w tym wątku. Dzięki.
Gus
Wdrożyłem to podejście i działa ono zgodnie z oczekiwaniami: druga instancja staje się zerowa. Nie musiałem jednak również rozszerzać konkretnej klasy. Właśnie zaimplementowałem Singleton :: instance () w konstruktorze tej konkretnej klasy.
snaphuman
w instancefunkcji $instancepowinien być nullniefalse
Mifas
Tak, ale to nie jest funkcja, ale metoda.
Abraham Tugałow
26

Prawdopodobnie powinieneś dodać prywatną metodę __clone (), aby uniemożliwić klonowanie instancji.

private function __clone() {}

Jeśli nie podasz tej metody, możliwe staje się:

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

teraz $inst1! == $inst2- nie są już tym samym wystąpieniem.

Stefan Gehrig
źródło
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(){
  }
}

używać:

/**
 *  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) {
}

Jeśli używasz PHP 5.4: cecha jest opcją, więc nie musisz marnować hierarchii dziedziczenia, aby mieć wzorzec Singleton

a także zauważ, że niezależnie od tego, czy używasz cech, czy rozszerzasz klasę Singleton, jednym luźnym celem było utworzenie singletonu klas potomnych, jeśli nie dodasz następującego wiersza kodu:

   protected static $inst = null;

w klasie dziecięcej

nieoczekiwany wynik to:

object(DBFactoryChild)#1 (0) {
}
object(DBFactoryChild)#1 (0) {
}
Jose Segura
źródło
10
protected  static $_instance;

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

Ten kod może obowiązywać dla dowolnej klasy bez względu na jej nazwę.

Hungneox
źródło
8

Obsługuje wiele obiektów z 1 linią na klasę:

Ta metoda wymusi singletony w każdej klasie, którą chcesz, ale musisz dodać 1 metodę do klasy, w której chcesz stworzyć singleton, a to zrobi to za ciebie.

To również przechowuje obiekty w klasie „SingleTonBase”, dzięki czemu można debugować wszystkie obiekty, które były używane w systemie przez rekursywne SingleTonBaseobiekty.


Utwórz plik o nazwie SingletonBase.php i dołącz go do katalogu głównego skryptu!

Kod jest

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;
    }
}

Następnie dla każdej klasy, która ma być singletonem, po prostu dodaj tę małą pojedynczą metodę.

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

Oto mały przykład:

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();

Możesz po prostu dodać tę funkcję singleton do dowolnej klasy, która utworzy tylko 1 instancję na klasę.

UWAGA: Zawsze powinieneś ustawić prywatność __construct, aby wyeliminować użycie nowej klasy (); instancje.

RobertPitt
źródło
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();
Rizon
źródło
5

Tak naprawdę nie musisz używać wzorca Singleton, ponieważ uważa się go za antypattern. Zasadniczo istnieje wiele powodów, aby w ogóle nie wdrażać tego wzorca. Przeczytaj to na początek: Najlepsza praktyka na lekcjach singletona PHP .

Jeśli mimo wszystko nadal uważasz, że musisz użyć wzorca Singleton, moglibyśmy napisać klasę, która pozwoli nam uzyskać funkcjonalność Singleton poprzez rozszerzenie naszej abstrakcyjnej klasy SingletonClassVendor.

Po to przyszedłem, aby rozwiązać ten problem.

<?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() {}

}

Użyj przykładu:

/**
 * 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
    }
}

Aby udowodnić, że działa zgodnie z oczekiwaniami:

/**
 *  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
DevWL
źródło
Czytając więcej pozytywnych odpowiedzi, miałem na myśli coś takiego. Na szczęście było już tutaj :)
hatef
3

Cała ta złożoność („późne wiązanie statyczne” ... harumph) jest dla mnie po prostu oznaką zepsutego modelu obiektu / klasy PHP. Gdyby obiekty klasy były obiektami pierwszej klasy (patrz Python), wówczas „$ _instance” byłby zmienną instancji klasy - członkiem obiektu klasy, w przeciwieństwie do elementu / właściwości jego instancji, a także jako elementu współdzielonego przez jego potomków. W świecie Smalltalk jest to różnica między „zmienną klasy” a „zmienną instancji klasy”.

W PHP wydaje mi się, że musimy wziąć sobie do serca wskazówkę, że wzorce są przewodnikiem w pisaniu kodu - możemy być może pomyśleć o szablonie Singleton, ale próbować napisać kod, który odziedziczy z faktycznej klasy „Singleton” wygląda na mylnie dla PHP (choć przypuszczałem, że jakaś przedsiębiorcza dusza mogłaby stworzyć odpowiednie słowo kluczowe SVN).

Będę nadal kodować każdy singleton osobno, korzystając ze wspólnego szablonu.

Zauważ, że absolutnie trzymam się Z dala od dyskusji o singlach-złych, życie jest za krótkie.

Tom Stambaugh
źródło
Twoje uwagi są słuszne, gdy patrzysz na coraz większą złożoność języka PHP. Wygląda na to, że dodaje się zbyt wiele nowych słów kluczowych, aby wyjść z zbyt wielu różnych dziur projektowych w zbyt wielu różnych paradygmatach kodowania. Co gorsza, ze względu na dużą szybkość zmian i przekrzywienie wersji na hostach i platformach programistycznych, dzisiejsze „rozwiązanie du Jour” (jak cechy w odpowiedzi @Eric Anderson [ stackoverflow.com/a/23998306/3696363] ) nie działa w systemach produkcyjnych, w których może działać „stabilna” wersja zamiast „najnowszej, najlepszej”.
Eliyahu Skoczylas
2

Wiem, że prawdopodobnie spowoduje to niepotrzebną wojnę płomieni, ale widzę, jak możesz chcieć więcej niż jednego połączenia z bazą danych, więc zgodziłbym się z tym, że singleton może nie być najlepszym rozwiązaniem na to ... jednak istnieją inne zastosowania wzorca singletonu, które uważam za niezwykle przydatne.

Oto przykład: postanowiłem stworzyć własny silnik MVC i silnik szablonów, ponieważ chciałem czegoś naprawdę lekkiego. Jednak dane, które chcę wyświetlić, zawierają wiele specjalnych znaków matematycznych, takich jak ≥ i μ i co masz ... Dane są przechowywane jako rzeczywisty znak UTF-8 w mojej bazie danych, a nie kodowane wcześniej niż HTML, ponieważ moja aplikacja może dostarczać inne formaty, takie jak PDF i CSV oprócz HTML. Odpowiednie miejsce do formatowania HTML znajduje się w szablonie („widok”, jeśli chcesz), który jest odpowiedzialny za renderowanie tej sekcji strony (fragmentu). Chcę przekonwertować je na odpowiednie encje HTML, ale funkcja get_html_translation_table () PHP nie jest super szybka. Lepiej jest raz pobrać dane i zapisać je jako tablicę, dzięki czemu będą dostępne dla wszystkich. Tutaj' próbka, którą zapukałem, żeby przetestować prędkość. Przypuszczalnie działałoby to niezależnie od tego, czy inne metody, których używasz (po uzyskaniu instancji), są statyczne, czy nie.

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";

Zasadniczo widziałem typowe wyniki:

test php. php
Czas działania: 27,842966794968 sekund przy użyciu singletonu
Czas działania: 237,78191494942 sekund bez użycia singletonu

Tak więc, chociaż z pewnością nie jestem ekspertem, nie widzę wygodniejszego i bardziej niezawodnego sposobu na ograniczenie narzutu powolnych wywołań pewnego rodzaju danych, jednocześnie czyniąc go bardzo prostym (jedna linia kodu, aby zrobić to, czego potrzebujesz). Przyznaję, że mój przykład ma tylko jedną przydatną metodę i dlatego nie jest lepszy niż funkcja zdefiniowana globalnie, ale jak tylko będziesz mieć dwie metody, będziesz chciał je zgrupować, prawda? Czy jestem daleko od bazy?

Wolę też przykłady, które faktycznie ZROBIJĄ, ponieważ czasami trudno jest wyobrazić sobie, kiedy przykład zawiera stwierdzenia takie jak „// zrób coś przydatnego tutaj”, które widzę cały czas podczas wyszukiwania samouczków.

Tak czy inaczej, chętnie przedstawię wszelkie uwagi lub komentarze na temat tego, dlaczego używanie singletonów do tego typu rzeczy jest szkodliwe (lub zbyt skomplikowane).

użytkownik2009125
źródło
1

Ten artykuł obejmuje dość obszernie temat: http://www.phptherightway.com/pages/Design-Patterns.html#singleton

Uwaga:

  • Konstruktor __construct()został zadeklarowany jako protecteduniemożliwiający utworzenie nowej instancji poza klasą za pośrednictwem newoperatora.
  • Metoda magiczna __clone()została zadeklarowana jako privatezapobiegająca klonowaniu instancji klasy za pośrednictwem cloneoperatora.
  • Metoda magiczna __wakeup()została zadeklarowana jako privatezapobiegająca odserializowaniu instancji klasy za pomocą funkcji globalnej unserialize().
  • Nowa instancja jest tworzona przez późne wiązanie statyczne w metodzie tworzenia statycznego getInstance()za pomocą słowa kluczowego static. Pozwala to na podklasę class Singletonw przykładzie.
Krzysztof Przygoda
źródło
1

Napisałem dawno temu, aby podzielić się tutaj

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();
Gyaneshwar Pardhi
źródło
0

Zgadzam się z pierwszą odpowiedzią, ale chciałbym również zadeklarować klasę jako ostateczną, aby nie można było jej rozszerzyć, ponieważ przedłużenie singletonu narusza wzorzec singletonu. Również zmienna instancji powinna być prywatna, aby nie można było uzyskać do niej bezpośredniego dostępu. Ustaw także metodę __clone jako prywatną, aby nie można było sklonować obiektu singleton.

Poniżej znajduje się przykładowy kod.

/**
 * 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;
    }
}

Przykładowe użycie

$user_factory = UserFactory::getInstance();

Co powstrzymuje cię przed zrobieniem tego (co naruszałoby wzorzec singletonu ...

NIE MOŻESZ TEGO ZROBIĆ!

$user_factory = UserFactory::$_instance;

class SecondUserFactory extends UserFactory { }
Joseph Crawford
źródło
0

To powinna być właściwa droga Singletona.

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(){

    }

} 
Mário Kapusta
źródło
0

Podobał mi się sposób użycia cech @ jose-segura, ale nie podobała mi się potrzeba definiowania zmiennej statycznej dla podklas. Poniżej znajduje się rozwiązanie, które pozwala tego uniknąć poprzez buforowanie instancji w statycznej zmiennej lokalnej do metody fabrycznej indeksowanej według nazwy klasy:

<?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() {}
}

Użycie jest takie samo jak @ jose-segura, tylko brak potrzeby zmiennej statycznej w podklasach.

Eric Anderson
źródło
0

Klasa bazy danych, która sprawdza, czy istnieje instancja bazy danych, zwróci poprzednią instancję.

   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] 

*/  

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

sunil rajput
źródło
0

To jest przykład tworzenia singletona w klasie Database

wzory projektowe 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);

to out put to -

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

użyj tylko jednej instancji, nie twórz 3 instancji

Surendra Kumar Ahir
źródło
0

Szybki przykład:

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;
    }
}

Mam nadzieję, że pomoc.

Dmitry
źródło
-4

Oto mój przykład, który zapewnia możliwość wywoływania jako $ var = new Singleton (), a także tworzenie 3 zmiennych w celu przetestowania, czy tworzy nowy obiekt:

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();
bboydev
źródło
5
Tyle że nie jest to singleton. Możesz utworzyć wiele instancji klasy Singleton.
Andrew Moore,
Myślę, że tak jest, ponieważ bez względu na to, która instancja wpływa na klasę Singleton, zmiany dotyczą wszystkich instancji Singleton. Dodałem dwie dodatkowe funkcje powyżej. Teraz spróbujmy zmodyfikować dane w jednym wystąpieniu i sprawdź pozostałe. Czy to nie Singleton, a jeśli nie - co jest nie tak?
bboydev
5
Singleton to klasa, która pozwala tylko na jedną instancję. Tworząc wiele instancji, unieważniasz tę zasadę.
Andrew Moore,