【问题标题】:Creating the Singleton design pattern in PHP5在 PHP5 中创建单例设计模式
【发布时间】:2010-09-17 05:30:21
【问题描述】:

如何使用 PHP5 类创建单例类?

【问题讨论】:

  • @Andrew 不要实例化连接到数据库的第二个实例。将该实例传递到需要它的地方。对 Singleton 的需求是 Code Smell。更多gooh.posterous.com/singletons-in-php
  • @Andrew Mmmmkay。无意冒犯,但我建议您在继续讨论之前先买一本关于软件质量的书。单例不会简化正常的维护和开发,反而会使其复杂化。事实上,恰恰相反:单元测试首先简化并支持开发。
  • @Andrew:您现在假设您只需要一个数据库连接。当您的需求发生变化并且您实际上需要与 2 个数据库服务器通信时会发生什么?更不用说如果你不能相信你的团队会正确做事,创建一个单例对你一点帮助也没有。从一开始就做正确的事,组建一个你可以信任的团队,你会没事的。
  • 仅仅因为 Singleton 被过度使用并不意味着它是一个应该避免的坏模式。不要讨厌单身人士。有时它是对某个问题的完美解决方案。最好开始争论为什么我们不应该使用它,而不是仅仅在情感上试图贬低它。

标签: php oop design-patterns singleton


【解决方案1】:
/**
 * 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; 的原因。

【讨论】:

  • 比较你应该使用 === 而不是 == 的两个实例。如果 $fact1 和 $fact2 都属于同一个类,== 将返回 true,但 === 仅在它们都是同一对象的相同实例时才返回 true。
  • 克隆方法也应该是私有的
  • 这个方法不会在每次调用Instance()时都将UserFactory的实例重置为null吗?在 java 中,$inst 变量将是一个私有静态属性,不应一遍又一遍地重置,否则你最好不要让它成为单例。
  • 这里有一篇很好的文章,说明了为什么以及如何在函数中将变量声明为静态变量按作者的意图工作:php.net/manual/en/…
  • 你应该使用 $inst = new self();不是 $inst = new UserFactory();对于以后遇到此问题的任何人。 +1 使用内置的 PHP 方法。
【解决方案2】:

不幸的是,当有多个子类时,Inwdr's answer 会中断。

这是一个正确的可继承 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";

【讨论】:

  • 这是迄今为止最接近正确的单例实现。您还应该考虑使用__wakeup() 方法来防止反序列化。
  • 实际上,您必须手动抛出异常或引发错误 - 将函数声明为受保护/私有只会引发 E_WARNING 表示它无法访问该方法,否则会继续。
  • 谢谢。我通常会将所有警告等都变成异常,所以我在测试时忘记了区别:P
  • 这是我发现的唯一能够正确处理多个子类的解决方案。谢谢!
【解决方案3】:

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 的一个实例。

【讨论】:

  • 后期静态绑定在php 5.3中确实是一件很不错的事情。可惜我还是不能用。
  • 来自@ggsonic:"subclass should own its own static var. check this: echo get_class(Foobar::getInstance());echo get_class(Singleton::getInstance());"
  • 这根本行不通,恰好 Foobar 是你构建的第一个类?
  • 仍有可能克隆..... "$a=Singleton::getInstance(); $b=unserialize(serialize($a)); $a!==$b;"
  • 当有多个子类时这不起作用! $instance 位于 Singleton,而不是子类中。实例化某个子类后,getInstance() 将为所有子类返回该实例。
【解决方案4】:

真正的现代的制作单例模式的方法是:

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

如您所见,这种实现更加灵活。

【讨论】:

  • 这是关于这个线程中单例模式的最清晰的答案。谢谢。
  • 我已经实现了这种方法,它按预期工作:第二个实例变为空。但是我也不需要扩展具体类。我刚刚在该具体类的构造函数中实现了 Singleton::instance()。
  • instance函数中$instance应该是null而不是false
  • 是的,但不是功能,而是方法。
  • 至少 __wakeup()__sleep() 现在必须是公开的。你当然可以在这些方法中抛出异常。
【解决方案5】:

您可能应该添加一个私有 __clone() 方法来禁止克隆实例。

private function __clone() {}

如果您不包含此方法,则可能出现以下情况

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

现在 $inst1 !== $inst2 - 它们不再是同一个实例了。

【讨论】:

    【解决方案6】:
    <?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;
    

    回复:

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

    如果您使用的是 PHP 5.4:trait 这是一个选项,因此您不必浪费继承层次结构来获得 单例模式

    还要注意,无论您使用 traits 还是 extends Singleton 类 如果您不添加以下代码行,一个松散的结局是创建子类的单例:

       protected static $inst = null;
    

    在子类中

    意想不到的结果是:

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

    【讨论】:

      【解决方案7】:
      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();
        

        你可以在你拥有的任何类中添加这个单例函数,它只会为每个类创建 1 个实例。

        注意:您应该始终将 __construct 设为私有以消除使用 new Class();实例化。

        【讨论】:

          【解决方案9】:
          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();
          

          【讨论】:

            【解决方案10】:

            您实际上并不需要使用单例模式,因为它被认为是一种反模式。基本上有很多理由根本不实施这种模式。从以下内容开始阅读:Best practice on PHP singleton classes

            如果你仍然认为你需要使用单例模式,那么我们可以编写一个类,通过扩展我们的 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
            

            【讨论】:

            • 在阅读更多赞成的答案时,我想到了这样的事情。幸运的是它已经在这里了:)
            【解决方案11】:

            对我来说,所有这些复杂性(“后期静态绑定”...harumph)只是 PHP 损坏的对象/类模型的标志。如果类对象是一等对象(参见 Python),那么 "$_instance" 将是类 instance 变量——类对象的成员,而不是其实例的成员/属性,并且也与其后代共享。在 Smalltalk 世界中,这就是“类变量”和“类实例变量”之间的区别。

            在 PHP 中,在我看来,我们似乎需要牢记模式是编写代码的指南——我们可能会考虑使用单例模板,但尝试编写继承自实际“ Singleton" 类看起来对 PHP 来说是错误的(尽管我认为一些有进取心的人可以创建一个合适的 SVN 关键字)。

            我将继续使用共享模板单独编写每个单例代码。

            请注意,我绝对不会参与单身人士是邪恶的讨论,生命太短暂了。

            【讨论】:

            • 在查看 PHP 语言日益复杂的情况时,您的评论是正确的。似乎添加了太多新的关键字来解决太多不同编码范例中太多不同的设计漏洞。更糟糕的是,由于主机和开发平台之间的高变化率和版本偏差,今天的“解决方案”(如@Eric Anderson 的回答 [stackoverflow.com/a/23998306/3696363], 中的特征)不适用于可能正在运行的生产系统“稳定”版本,而不是“最新、最棒”的版本。
            【解决方案12】:

            我知道这可能会导致一场不必要的火焰战争,但我可以看到您可能需要多个数据库连接,所以我承认单例可能不是最好的解决方案......但是,我发现单例模式还有其他用途非常有用。

            这是一个例子:我决定推出自己的 MVC 和模板引擎,因为我想要一些真正轻量级的东西。但是,我要显示的数据包含许多特殊的数学字符,例如 ≥ 和 μ 以及你有什么......数据在我的数据库中存储为实际的 UTF-8 字符,而不是预 HTML 编码,因为除了 HTML,我的应用程序还可以提供其他格式,例如 PDF 和 CSV。对 HTML 进行格式化的适当位置是在负责呈现该页面部分 (sn-p) 的模板(“视图”,如果您愿意的话)内。我想将它们转换为适当的 HTML 实体,但 PHP 的 get_html_translation_table() 函数不是很快。一次检索数据并将其存储为数组更有意义,以供所有人使用。这是我拼凑在一起测试速度的样本。据推测,无论您使用的其他方法(在获取实例之后)是否是静态的,这都会起作用。

            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 秒,不使用单例
            

            因此,虽然我当然不是专家,但我没有看到一种更方便可靠的方法来减少对某种数据的缓慢调用的开销,同时使其超级简单(单行代码即可完成您的工作需要)。当然,我的示例只有一个有用的方法,因此并不比全局定义的函数好,但是一旦您有两种方法,您就会想要将它们组合在一起,对吗?我离基地很远吗?

            另外,我更喜欢实际做某事的示例,因为有时当示例包含诸如“//在这里做一些有用的事情”之类的语句时,我很难想象这些语句,我在搜索教程时总是看到。

            无论如何,我希望得到任何反馈或 cmets,说明为什么将单例用于此类事情是有害的(或过于复杂)。

            【讨论】:

              【解决方案13】:

              这篇文章涵盖了相当广泛的主题: http://www.phptherightway.com/pages/Design-Patterns.html#singleton

              注意以下几点:

              • 构造函数__construct() 被声明为protected 以防止通过new 运算符在类之外创建新实例。
              • 魔术方法__clone() 被声明为private 以防止通过clone 运算符克隆类的实例。
              • 魔术方法__wakeup() 被声明为private 以防止通过全局函数对类的实例进行反序列化 unserialize()
              • 在静态创建方法getInstance() 中使用关键字static 通过后期静态绑定创建一个新实例。这 允许对示例中的 class Singleton 进行子类化。

              【讨论】:

                【解决方案14】:

                我已经写了很久的想法在这里分享

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

                【讨论】:

                  【解决方案15】:

                  我同意第一个答案,但我也会将该类声明为 final,以便它不能被扩展,因为扩展单例违反了单例模式。此外,实例变量应该是私有的,因此不能直接访问。还要将 __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 { }
                  

                  【讨论】:

                    【解决方案16】:

                    这应该是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(){
                    
                        }
                    
                    } 
                    

                    【讨论】:

                      【解决方案17】:

                      我喜欢@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 相同,只是子类中不需要静态变量。

                      【讨论】:

                        【解决方案18】:

                        检查是否存在任何现有数据库实例的数据库类,它将返回先前的实例。

                           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

                        【讨论】:

                          【解决方案19】:

                          这是在数据库类上创建单例的示例

                          设计模式 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 个实例

                          【讨论】:

                            【解决方案20】:

                            快速示例:

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

                            希望帮助。

                            【讨论】:

                              【解决方案21】:

                              上面的答案还可以,但我会补充更多。

                              无论谁在 2021 年来到这里,我将展示另一个使用 Singleton Pattern 类作为 trait 并在任何类中重复使用它的示例。

                              <?php
                              
                              namespace Akash;
                              
                              trait Singleton
                              {
                                  /**
                                   * Singleton Instance
                                   *
                                   * @var Singleton
                                   */
                                  private static $instance;
                              
                                  /**
                                   * Private Constructor
                                   *
                                   * We can't use the constructor to create an instance of the class
                                   *
                                   * @return void
                                   */
                                  private function __construct()
                                  {
                                      // Don't do anything, we don't want to be initialized
                                  }
                              
                                  /**
                                   * Get the singleton instance
                                   *
                                   * @return Singleton
                                   */
                                  public static function getInstance()
                                  {
                                      if (!isset(self::$instance)) {
                                          self::$instance = new self();
                                      }
                              
                                      return self::$instance;
                                  }
                              
                                  /**
                                   * Private clone method to prevent cloning of the instance of the
                                   * Singleton instance.
                                   *
                                   * @return void
                                   */
                                  private function __clone()
                                  {
                                      // Don't do anything, we don't want to be cloned
                                  }
                              
                                  /**
                                   * Private unserialize method to prevent unserializing of the Singleton
                                   * instance.
                                   *
                                   * @return void
                                   */
                                  private function __wakeup()
                                  {
                                      // Don't do anything, we don't want to be unserialized
                                  }
                              }
                              

                              所以,像在任何课程中一样轻松使用它。假设我们要在UserSeeder 类中实现单例模式。

                              <?php
                              
                              class UserSeeder
                              {
                                  use Singleton;
                              
                                  /**
                                   * Seed Users
                                   *
                                   * @return void
                                   */
                                  public function seed()
                                  {
                                      echo 'Seeding...';
                                  }
                              }
                              

                              【讨论】:

                              • 您似乎有多个包含文章链接的答案。当链接到您自己的网站或内容(或您附属的内容)时,您must disclose your affiliation in the answer 以免被视为垃圾邮件。根据 Stack Exchange 政策,在您的用户名中包含与 URL 相同的文本或在您的个人资料中提及它不被视为充分披露。请在这些答案中公开编辑。
                              【解决方案22】:

                              这是我的示例,它提供了调用 $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();
                              

                              【讨论】:

                              • 除非它不是单例。您可以创建类 Singleton 的多个实例。
                              • 我想毕竟是这样,因为无论哪个实例影响 Singleton 类,所有的 Singleton 实例都会发生变化。我在上面添加了另外两个功能。现在,让我们尝试在一个实例中修改数据并检查其他实例。那么,这不是单例吗?如果不是 - 什么是不正确的?
                              • 单例是一个类,它只允许一个自身的实例。通过创建多个实例,您将放弃该原则。
                              猜你喜欢
                              • 2020-01-17
                              • 2017-10-12
                              • 1970-01-01
                              • 1970-01-01
                              • 1970-01-01
                              • 2010-11-03
                              相关资源
                              最近更新 更多