การสร้างรูปแบบการออกแบบ Singleton ใน PHP5


204

เราจะสร้างคลาส Singleton โดยใช้คลาส PHP5 ได้อย่างไร



1
@Andrew Dont ยกตัวอย่างอินสแตนซ์ที่สองที่เชื่อมต่อกับฐานข้อมูลแล้ว ส่งผ่านอินสแตนซ์นั้นไปยังที่ที่จำเป็น ความต้องการ Singleton คือ Code Smell ข้อมูลเพิ่มเติมได้ที่gooh.posterous.com/singletons-in-php
Gordon

3
@Andrew Mmmmkay ไม่มีความผิด แต่ฉันขอแนะนำให้คุณรับหนังสือเกี่ยวกับคุณภาพของซอฟต์แวร์ก่อนที่เราจะดำเนินการอภิปรายต่อไป Singletons ไม่ลดความซับซ้อน แต่ทำให้ยุ่งยากในการบำรุงรักษาและการพัฒนาตามปกติ ในความเป็นจริงมันเป็นอีกด้านหนึ่ง: เป็นการทดสอบหน่วยที่ทำให้ง่ายขึ้นและสามารถพัฒนาได้ตั้งแต่แรก
Gordon

3
@Andrew: คุณคิดว่าตอนนี้คุณต้องการเพียงหนึ่งการเชื่อมต่อฐานข้อมูล จะเกิดอะไรขึ้นเมื่อความต้องการของคุณเปลี่ยนแปลงและคุณต้องพูดคุยกับเซิร์ฟเวอร์ฐานข้อมูล 2 แห่ง ไม่ต้องพูดถึงหากคุณไม่สามารถไว้วางใจทีมของคุณให้ทำสิ่งที่ถูกต้องการสร้างซิงเกิลตันจะไม่ช่วยคุณอย่างน้อยที่สุด ทำสิ่งต่าง ๆ ตั้งแต่เริ่มต้นและรับทีมที่คุณสามารถไว้วางใจได้และคุณจะไม่เป็นไร
ircmaxell

4
เพียงเพราะ Singleton นั้นถูกใช้มากเกินไปไม่ได้ทำให้เป็นรูปแบบที่ไม่ดีที่ควรหลีกเลี่ยง อย่าเกลียดที่ซิงเกิล บางครั้งมันเป็นทางออกที่ดีอย่างสมบูรณ์ในการแก้ไขปัญหาบางอย่าง เริ่มถกเถียงกันดีกว่าว่าทำไมเราไม่ควรใช้มันแทนที่จะรู้สึกว่ามันพยายามทำให้เสื่อมเสีย
Gilles Lesire

คำตอบ:


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

    }
}

ใช้:

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

$fact == $fact2;

แต่:

$fact = new UserFactory()

โยนข้อผิดพลาด

ดูhttp://php.net/manual/en/language.variables.scope.php#language.variables.scope.staticเพื่อทำความเข้าใจขอบเขตตัวแปรสแตติกและทำไมการตั้งค่าใช้static $inst = null;งาน


59
เพื่อเปรียบเทียบสองอินสแตนซ์ที่คุณควรใช้ === มากกว่า == == จะส่งกลับค่าจริงถ้า $ fact1 และ $ fact2 เป็นทั้งคู่ในชั้นเรียนเดียวกัน แต่ === จะคืนค่าจริงถ้าทั้งคู่เป็นอินสแตนซ์เดียวกันของวัตถุเดียวกัน
Keith Twombley

10
วิธีการโคลนควรเป็นแบบส่วนตัวด้วย
Petrov อเล็กซ์

22
วิธีนี้จะไม่รีเซ็ตอินสแตนซ์ของ UserFactory เป็นโมฆะทุกครั้งที่คุณเรียกใช้อินสแตนซ์ () ใน java ตัวแปร $ inst จะเป็นแอตทริบิวต์แบบคงที่ส่วนตัวซึ่งไม่ควรรีเซ็ตซ้ำแล้วซ้ำอีกไม่เช่นนั้นคุณอาจไม่ได้ทำให้เป็นซิงเกิลตัน
Rudy Garcia

8
นี่คือการเขียนที่ดีว่าเพราะเหตุใดและวิธีการประกาศตัวแปรในรูปแบบสแตติกในฟังก์ชั่นทำงานอย่างไรเมื่อผู้เขียนตั้งใจ: php.net/manual/en/…
hereswhatidid

10
คุณควรใช้ $ inst = new self (); ไม่ใช่ $ inst = new UserFactory (); สำหรับทุกคนที่เจอสิ่งนี้ในภายหลัง +1 สำหรับการใช้วิธีการ PHP ในตัว
Ligemer

119

PHP 5.3 อนุญาตให้สร้างคลาส Singleton ที่สืบทอดได้ผ่านการโยงแบบคงที่ในช่วงปลาย

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

วิธีนี้จะช่วยแก้ปัญหาก่อนที่ PHP 5.3 คลาสใด ๆ ที่ขยาย Singleton จะสร้างอินสแตนซ์ของคลาสพาเรนต์แทนที่จะเป็นของตัวเอง

ตอนนี้คุณสามารถทำได้:

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

และ $ foo จะเป็นตัวอย่างของ Foobar แทนที่จะเป็นตัวอย่างของ Singleton


1
การผูกแบบคงที่ภายหลังเป็นสิ่งที่ดีมากใน php 5.3 เสียดายที่ฉันยังใช้มันไม่ได้
AntonioCS

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

4
มันใช้งานไม่ได้เลยมันเกิดขึ้นแค่ไหนที่ Foobar เป็นชั้นหนึ่งที่คุณสร้างขึ้น?
Chris KL

1
ยังคงมีความเป็นไปได้ที่จะโคลน ..... "$ a = Singleton :: getInstance (); $ b = ยกเลิกการระบุ (อนุกรม ($ a)); $ a! == $ b;"
bortunac

15
สิ่งนี้ไม่ทำงานเมื่อมีคลาสย่อยมากกว่าหนึ่งคลาส! $instanceอยู่ใน Singleton ไม่ใช่คลาสย่อย หลังจากคลาสย่อยบางอินสแตนซ์ getInstance () จะส่งคืนอินสแตนซ์นั้นสำหรับคลาสย่อยทั้งหมด
mpartel

116

น่าเสียดายที่คำตอบของ Inwdrหยุดลงเมื่อมีคลาสย่อยหลายคลาส

นี่คือคลาสพื้นฐานที่ถูกต้องซึ่งสืบทอดได้ของ 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];
    }
}

รหัสทดสอบ:

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

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

1
นี่คือจุดที่ใกล้เคียงที่สุดเพื่อแก้ไขการใช้งานซิงเกิลตัน คุณควรพิจารณาการขว้างปาด้วยวิธี__wakeup ()เพื่อป้องกันการไม่ระบุตัวตน
Robert Rossmann

ที่จริงแล้วคุณต้องโยนข้อยกเว้นหรือยกข้อผิดพลาดด้วยตนเอง - การประกาศฟังก์ชั่นในฐานะที่ได้รับการป้องกัน / ส่วนตัวจะเพิ่ม E_WARNING เพียงบอกว่ามันไม่สามารถเข้าถึงวิธีการ แต่จะดำเนินการต่อ
Robert Rossmann

ขอบคุณ ปกติฉันจะมีคำเตือน ฯลฯ กลายเป็นข้อยกเว้นดังนั้นฉันลืมเกี่ยวกับความแตกต่างเมื่อฉันทดสอบ: P
mpartel

นี่เป็นทางออกเดียวที่ฉันพบว่าจัดการกับคลาสย่อยได้อย่างเหมาะสม ขอบคุณ!
Bob Dankert

36

วิธีที่แท้จริงและทันสมัยในการสร้างรูปแบบซิงเกิลคือ:

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

}

ดังนั้นตอนนี้คุณสามารถใช้มันเหมือน

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

เมื่อคุณเห็นการรับรู้นี้มีความยืดหยุ่นมากขึ้น


4
นี่คือคำตอบที่ชัดเจนที่สุดเกี่ยวกับรูปแบบซิงเกิลภายในเธรดนี้ ขอบคุณ
กัส

ฉันใช้วิธีนี้และใช้งานได้แล้ว: ตัวอย่างที่สองกลายเป็นโมฆะ อย่างไรก็ตามฉันไม่ต้องการขยายชั้นรูปธรรมเช่นกัน ฉันเพิ่งใช้ Singleton :: instance () ในตัวสร้างของคลาสคอนกรีตนั้น
snaphuman

ในinstanceฟังก์ชั่น$instanceที่ควรจะเป็นnullไม่ได้false
Mifas

ใช่ แต่มันไม่ใช่ฟังก์ชั่น แต่เป็นวิธีการ
Abraham Tugalov

26

คุณควรเพิ่มเมธอด __clone () ส่วนตัวเพื่อไม่อนุญาตการโคลนอินสแตนซ์

private function __clone() {}

หากคุณไม่ได้รวมวิธีนี้สิ่งต่อไปนี้จะเป็นไปได้

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

ตอนนี้$inst1! == $inst2- พวกเขาไม่ใช่อินสแตนซ์เดียวกันอีกต่อไป


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

ใช้:

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

หากคุณกำลังใช้ PHP 5.4: ลักษณะตัวเลือกของคุณจึงไม่ต้องเสียลำดับชั้นมรดกเพื่อให้มีรูปแบบเดี่ยว

และยังสังเกตเห็นว่าไม่ว่าคุณจะใช้คุณสมบัติหรือขยายชั้นเดียวที่มีปลายด้านเดียวที่หลวมก็คือการสร้างชั้นเรียนสำหรับเด็กถ้าคุณไม่เพิ่มบรรทัดของรหัสต่อไปนี้:

   protected static $inst = null;

ในชั้นเรียนของเด็ก

ผลลัพธ์ที่ไม่คาดคิดจะเป็น:

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

รหัสนี้สามารถใช้กับคลาสใดก็ได้โดยไม่ต้องสนใจชื่อคลาส


8

รองรับหลายออบเจคด้วย 1 บรรทัดต่อคลาส:

วิธีนี้จะบังคับใช้ซิงเกิลตันในคลาสใด ๆ ที่คุณต้องการและคุณต้องทำคือเพิ่ม 1 วิธีในคลาสที่คุณต้องการสร้างซิงเกิลตันและจะทำเพื่อคุณ

สิ่งนี้ยังเก็บวัตถุในคลาส "SingleTonBase" เพื่อให้คุณสามารถดีบักวัตถุทั้งหมดของคุณที่คุณใช้ในระบบของคุณด้วยการเรียกSingleTonBaseวัตถุอีกครั้ง


สร้างไฟล์ชื่อ SingletonBase.php และรวมไว้ในรูทของสคริปต์ของคุณ!

รหัสคือ

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

จากนั้นสำหรับคลาสใด ๆ ที่คุณต้องการสร้างซิงเกิลตันให้เพิ่มเมธอดเดี่ยวขนาดเล็กนี้

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

นี่คือตัวอย่างเล็ก ๆ :

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

และคุณสามารถเพิ่มฟังก์ชัน singleton นี้ในคลาสใดก็ได้ที่คุณมีและจะสร้าง 1 อินสแตนซ์ต่อคลาสเท่านั้น

หมายเหตุ: คุณควรสร้าง __ โครงสร้างส่วนตัวเพื่อกำจัดการใช้คลาสใหม่ (); instantiations


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

คุณไม่จำเป็นต้องใช้รูปแบบซิงเกิลจริงๆเพราะถือว่าเป็น Antipattern โดยทั่วไปมีเหตุผลมากมายที่จะไม่ใช้รูปแบบนี้เลย อ่านนี้เพื่อเริ่มต้นกับการปฏิบัติที่ดีที่สุดในชั้นเรียน PHP เดี่ยว

หากคุณยังคิดว่าคุณต้องใช้รูปแบบซิงเกิลแล้วเราสามารถเขียนคลาสที่จะช่วยให้เราได้รับฟังก์ชั่น Singleton โดยขยายคลาสนามธรรมของ SingletonClassVendor

นี่คือสิ่งที่ฉันมาพร้อมกับการแก้ปัญหานี้

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

}

ใช้ตัวอย่าง:

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

เพียงเพื่อพิสูจน์ว่าทำงานได้ตามที่คาดไว้:

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

ในขณะที่อ่านคำตอบ upvote เพิ่มเติมฉันมีบางอย่างเช่นนี้ในใจ โชคดีที่มันเป็นที่นี่แล้ว :)
hatef

3

ความซับซ้อนทั้งหมดนี้ ("การผูกปลายคงที่" ... ยากลำบาก) สำหรับฉันแล้วมันเป็นเพียงสัญลักษณ์ของโมเดลวัตถุ / คลาสที่เสียหายของ PHP ถ้าวัตถุคลาสเป็นวัตถุชั้นหนึ่ง (ดู Python) ดังนั้น "$ _instance" จะเป็นตัวแปรอินสแตนซ์ของคลาส- สมาชิกของวัตถุคลาสซึ่งตรงข้ามกับสมาชิก / คุณสมบัติของอินสแตนซ์และเมื่อเทียบกับที่ใช้ร่วมกัน โดยลูกหลานของมัน ในโลก Smalltalk นี่คือความแตกต่างระหว่าง "ตัวแปรคลาส" และ "ตัวแปรอินสแตนซ์ของคลาส"

ใน PHP ดูเหมือนว่าเราต้องคำนึงถึงคำแนะนำว่ารูปแบบเป็นแนวทางในการเขียนโค้ด - บางทีเราอาจคิดถึงเทมเพลต Singleton แต่พยายามเขียนโค้ดที่สืบทอดมาจากคลาส "Singleton" ที่แท้จริง ดูผิดสำหรับ PHP (แต่ฉันคิดว่าวิญญาณผู้กล้าได้กล้าเสียสามารถสร้างคำหลัก SVN ที่เหมาะสม)

ฉันจะยังคงรหัสแต่ละเดี่ยวแยกกันโดยใช้แม่แบบที่ใช้ร่วมกัน

สังเกตุว่าฉันไม่ได้อยู่ในการสนทนาเดี่ยวตันอย่างชั่วร้ายชีวิตสั้นเกินไป


คำพูดของคุณถูกต้องเมื่อดูความซับซ้อนที่เพิ่มขึ้นของภาษา PHP ดูเหมือนว่ามีการเพิ่มคำหลักใหม่จำนวนมากเกินไปเพื่อหลีกเลี่ยงช่องโหว่การออกแบบที่แตกต่างกันมากเกินไปในกระบวนทัศน์การเข้ารหัสที่แตกต่างกันมากเกินไป ที่แย่กว่านั้นเนื่องจากอัตราการเปลี่ยนแปลงที่สูงและเวอร์ชันที่เอียงข้ามโฮสต์และแพลตฟอร์มการพัฒนา "solution du jour" ของวันนี้ (เช่นลักษณะใน @Eric Anderson คำตอบ [ stackoverflow.com/a/23998306/3696363] ) ไม่ทำงาน บนระบบที่ใช้งานจริงที่อาจใช้งานเวอร์ชัน "เสถียร" แทนที่จะเป็น "ล่าสุดและยิ่งใหญ่ที่สุด"
Eliyahu Skoczylas

2

ฉันรู้ว่านี่อาจจะทำให้เกิดสงครามไฟที่ไม่จำเป็น แต่ฉันสามารถดูว่าคุณอาจต้องการการเชื่อมต่อฐานข้อมูลมากกว่าหนึ่งดังนั้นฉันจะยอมรับจุดที่ซิงเกิลอาจไม่ใช่วิธีที่ดีที่สุดสำหรับ ... อย่างไรก็ตามมี การใช้รูปแบบซิงเกิลอื่นที่ฉันพบว่ามีประโยชน์มาก

นี่คือตัวอย่าง: ฉันตัดสินใจที่จะม้วน MVC ของตัวเองและเครื่องมือสร้างแรงบิดเพราะฉันต้องการบางสิ่งที่มีน้ำหนักเบาจริงๆ อย่างไรก็ตามข้อมูลที่ฉันต้องการแสดงมีอักขระพิเศษทางคณิตศาสตร์จำนวนมากเช่น≥และμและสิ่งที่คุณมี ... ข้อมูลถูกเก็บเป็นอักขระ UTF-8 จริงในฐานข้อมูลของฉันแทนที่จะเข้ารหัส HTML ล่วงหน้าเนื่องจาก แอพของฉันสามารถส่งรูปแบบอื่น ๆ เช่น PDF และ CSV นอกเหนือจาก HTML ตำแหน่งที่เหมาะสมสำหรับการจัดรูปแบบ HTML อยู่ในเทมเพลต ("ดู" หากคุณต้องการ) ที่รับผิดชอบการแสดงผลส่วนของหน้านั้น (ตัวอย่าง) ฉันต้องการแปลงให้เป็นเอนทิตี HTML ที่เหมาะสม แต่ฟังก์ชัน get_html_translation_table () ของ PHP นั้นไม่เร็วมาก ทำให้รู้สึกดีขึ้นในการดึงข้อมูลหนึ่งครั้งและเก็บเป็นอาร์เรย์ทำให้ทุกคนสามารถใช้งานได้ นี่' ตัวอย่างฉันเคาะด้วยกันเพื่อทดสอบความเร็ว สมมุติว่าวิธีนี้ใช้ได้ผลโดยไม่คำนึงว่าวิธีอื่นที่คุณใช้ (หลังจากได้รับอินสแตนซ์) นั้นคงที่หรือไม่

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

โดยทั่วไปฉันเห็นผลลัพธ์ทั่วไปดังนี้:

php test.php
เวลาทำงาน: 27.842966794968 วินาทีโดยใช้ซิงเกิล
เวลาทำงาน: 237.78191494942 วินาทีโดยไม่ต้องใช้ซิงเกิล

ดังนั้นในขณะที่ฉันไม่มีผู้เชี่ยวชาญแน่นอนฉันไม่เห็นวิธีที่สะดวกและเชื่อถือได้มากขึ้นในการลดค่าใช้จ่ายในการโทรช้าสำหรับข้อมูลบางประเภทในขณะที่ทำให้มันง่ายมาก (รหัสบรรทัดเดียวเพื่อทำสิ่งที่คุณต้องการ) ให้ตัวอย่างของฉันมีวิธีที่มีประโยชน์เพียงวิธีเดียวเท่านั้นดังนั้นจึงไม่ดีไปกว่าฟังก์ชั่นที่กำหนดไว้ทั่วโลก แต่ทันทีที่คุณมีสองวิธีคุณจะต้องจัดกลุ่มพวกมันด้วยกันใช่ไหม? ฉันจะออกจากฐาน?

นอกจากนี้ฉันชอบตัวอย่างที่ทำอะไรบางอย่างจริง ๆ เพราะบางครั้งมันยากที่จะมองเห็นเมื่อตัวอย่างมีข้อความเช่น "// ทำบางสิ่งที่มีประโยชน์ที่นี่" ซึ่งฉันเห็นตลอดเวลาเมื่อค้นหาบทช่วยสอน

อย่างไรก็ตามฉันชอบความคิดเห็นหรือความคิดเห็นใด ๆ ว่าทำไมการใช้ซิงเกิลตันสำหรับสิ่งประเภทนี้จึงเป็นอันตราย (หรือซับซ้อนเกินไป)


1

บทความนี้ครอบคลุมหัวข้อค่อนข้างครอบคลุม: http://www.phptherightway.com/pages/Design-Patterns.html#singleton

หมายเหตุดังต่อไปนี้:

  • Constructor __construct()ถูกประกาศprotectedเพื่อป้องกันการสร้างอินสแตนซ์ใหม่นอกคลาสผ่านnewโอเปอเรเตอร์
  • วิธีเวทย์มนตร์__clone()ถูกประกาศprivateเพื่อป้องกันการโคลนอินสแตนซ์ของคลาสผ่านcloneโอเปอเรเตอร์
  • วิธีมายากล__wakeup()ถูกประกาศเป็นprivateเพื่อป้องกันไม่ให้ unserializing unserialize()ของตัวอย่างของการเรียนที่ผ่านฟังก์ชั่นระดับโลก
  • ตัวอย่างใหม่จะถูกสร้างขึ้นผ่านปลายคงมีผลผูกพันในวิธีการสร้างคงด้วยคำหลักgetInstance() staticสิ่งนี้อนุญาตให้ subclassing ของclass Singletonในตัวอย่าง

1

ฉันได้เขียนความคิดที่ยาวนานมาแบ่งปันที่นี่

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

ฉันเห็นด้วยกับคำตอบแรก แต่ฉันก็จะประกาศให้ชั้นเป็นขั้นสุดท้ายเพื่อที่จะไม่สามารถขยายได้เมื่อขยายซิงเกิลที่ละเมิดรูปแบบซิงเกิล นอกจากนี้ตัวแปรอินสแตนซ์ควรเป็นแบบส่วนตัวเพื่อให้ไม่สามารถเข้าถึงได้โดยตรง ทำให้เมธอด __clone เป็นไพรเวตเพื่อให้คุณไม่สามารถโคลนวัตถุซิงเกิล

ด้านล่างนี้คือตัวอย่างโค้ด

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

ตัวอย่างการใช้งาน

$user_factory = UserFactory::getInstance();

สิ่งนี้จะหยุดคุณจากการทำ (ซึ่งจะละเมิดรูปแบบซิงเกิล ..

คุณไม่สามารถทำเช่นนี้ได้!

$user_factory = UserFactory::$_instance;

class SecondUserFactory extends UserFactory { }

0

นี่ควรเป็นวิธีที่ถูกต้องของซิงเกิล

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

ฉันชอบวิธีการ @ jose-segura ของการใช้ลักษณะ แต่ไม่ชอบความจำเป็นในการกำหนดตัวแปรคงที่ในคลาสย่อย ด้านล่างนี้เป็นวิธีการแก้ปัญหาที่หลีกเลี่ยงได้โดยการแคชอินสแตนซ์ในตัวแปรสแตติกท้องถิ่นไปยังวิธีการจัดทำดัชนีจากโรงงานโดยชื่อคลาส:

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

การใช้งานเหมือนกับ @ jose-segura ไม่จำเป็นต้องมีตัวแปรสแตติกในคลาสย่อยเท่านั้น


0

คลาสฐานข้อมูลที่ตรวจสอบว่ามีอินสแตนซ์ฐานข้อมูลใด ๆ ที่มีอยู่จะส่งคืนอินสแตนซ์ก่อนหน้า

   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] 

*/  

อ้างอิงhttp://www.phptechi.com/php-singleton-design-patterns-example.html


0

นี่คือตัวอย่างของการสร้างซิงเกิลตันในคลาสฐานข้อมูล

รูปแบบการออกแบบ 1) ซิงเกิล

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

จากนั้นออกวางคือ -

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

ใช้อินสแตนซ์เดียวเท่านั้นที่ไม่ได้สร้าง 3 อินสแตนซ์


0

ตัวอย่างด่วน:

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

หวังว่าจะช่วย


-4

นี่คือตัวอย่างของฉันที่ให้ความสามารถในการเรียกใช้เป็น $ var = new Singleton () และสร้างตัวแปร 3 ตัวเพื่อทดสอบว่ามันสร้างวัตถุใหม่หรือไม่:

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
ยกเว้นว่าไม่ใช่ซิงเกิล คุณสามารถสร้างอินสแตนซ์ของคลาส Singleton ได้หลายอินสแตนซ์
Andrew Moore

ฉันคิดว่ามันเป็นเพราะทั้งหมดไม่ว่าอินสแตนซ์ใดที่ส่งผลกระทบต่อคลาส Singleton การเปลี่ยนแปลงนั้นเกิดขึ้นสำหรับอินสแตนซ์ทั้งหมดของ Singleton ฉันได้เพิ่มอีกสองฟังก์ชั่นด้านบน ตอนนี้เรามาลองแก้ไขข้อมูลในอินสแตนซ์หนึ่งและลองดูข้อมูลอื่น ๆ ดังนั้นมันไม่ได้เป็นซิงเกิลตันและถ้าไม่ใช่ - อะไรที่ไม่ถูกต้อง?
bboydev

5
ซิงเกิลตันเป็นคลาสที่อนุญาตเพียงหนึ่งอินสแตนซ์ของตัวเอง โดยการสร้างหลายอินสแตนซ์คุณจะโมฆะหลักการนั้น
Andrew Moore
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.