【问题标题】:What is singleton in PHP?PHP中的单例是什么?
【发布时间】:2014-09-11 04:37:25
【问题描述】:

对于许多人来说,这个问题可能真的是一个愚蠢的问题。但是,我找不到它的确切答案。

什么是 PHP 中的单例类?

我浏览了很多教程,仍然不明白它的确切含义。 我的理解是,它不能被多次实例化。对象将被共享。 那个的真实意义是什么?假设为数据库连接实现了单例,这是否意味着如果'A'正在访问该站点并登录。同时说'B'尝试登录并且B将无法登录,直到A注销并释放对象?

【问题讨论】:

  • 数据库连接并不是一个特别好的用例——我已经编写了几个必须连接到两个不同数据库服务器的应用程序。而且它并不是专门的 PHP 概念:它是一种 OOP 设计模式,您可以用任何 OOP 语言实现。
  • 您是否已经阅读过Singleton pattern at Wikipedia
  • Singleton 是基本的 PHP 设计模式之一,尽管有些人认为它是一种反模式,但它可以在一系列可能性中使用和发展:phpclasses.org/package/…

标签: php


【解决方案1】:

更多参考:Is there a use-case for singletons with database access in PHP?

来自Design Patterns - PHP: The Right Way

在设计 Web 应用程序时,通常在概念和架构上允许访问特定类的一个且仅一个实例是有意义的。单例模式使我们能够做到这一点。

<?php
class Singleton
{
    /**
     * Returns the *Singleton* instance of this class.
     *
     * @staticvar Singleton $instance The *Singleton* instances of this class.
     *
     * @return Singleton The *Singleton* instance.
     */
    public static function getInstance()
    {
        static $instance = null;
        if (null === $instance) {
            $instance = new static();
        }

        return $instance;
    }

    /**
     * Protected constructor to prevent creating a new instance of the
     * *Singleton* via the `new` operator from outside of this class.
     */
    protected function __construct()
    {
    }

    /**
     * Private clone method to prevent cloning of the instance of the
     * *Singleton* instance.
     *
     * @return void
     */
    private function __clone()
    {
    }

    /**
     * Private unserialize method to prevent unserializing of the *Singleton*
     * instance.
     *
     * @return void
     */
    private function __wakeup()
    {
    }
}

class SingletonChild extends Singleton
{
}

$obj = Singleton::getInstance();
var_dump($obj === Singleton::getInstance());             // bool(true)

$anotherObj = SingletonChild::getInstance();
var_dump($anotherObj === Singleton::getInstance());      // bool(false)

var_dump($anotherObj === SingletonChild::getInstance()); // bool(true)

上面的代码使用静态变量和静态创建方法getInstance() 实现了单例模式。请注意以下几点:

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

当我们需要确保在 Web 应用程序的整个请求生命周期中只有一个类的单个实例时,单例模式很有用。这通常发生在我们拥有全局对象(例如配置类)或共享资源(例如事件队列)时。

您在使用单例模式时应该小心,因为它的本质是将全局状态引入您的应用程序,从而降低了可测试性。在大多数情况下,可以(并且应该)使用依赖注入来代替单例类。使用依赖注入意味着我们不会在应用程序的设计中引入不必要的耦合,因为使用共享或全局资源的对象不需要了解具体定义的类。

Singleton pattern on Wikipedia

【讨论】:

  • 它不是来自维基百科,而是来自PHP: The Right Way。还是不好。
  • 我只是在更新链接:)
  • @Gumbo:好的,我来自移动设备,没有点击。正如你所说,是一样的
【解决方案2】:

单例是一种特殊的类,正如你所说的,它只能被实例化一次。

第一点:不是PHP相关的概念,而是OOP的概念。

“仅实例化一次”是什么意思?它只是意味着如果该类的对象已经实例化,系统将返回它而不是创建新对象。 为什么?因为,有时,您需要一个“通用”实例(全局实例),或者因为实例化一个已经存在的对象的“副本”是没有用的。

让我们首先考虑一个框架:在引导操作中,您需要实例化一个对象,但您可以(必须)与其他请求框架引导的人共享它。

对于第二种情况,让我们考虑一个只有方法而没有成员的类(因此基本上没有内部状态)。也许您可以将其实现为静态类,但如果您想遵循设计模式,请考虑 AbstractFactory)您应该使用对象。因此,没有必要拥有一些只有方法的相同对象的副本,而且也会浪费内存。

这是我使用单例的两个主要原因。

【讨论】:

  • @DonCallisto 如果我理解正确,这只是一种构建代码的方法,在事务完成之前与限制用户无关。我说的对吗?
  • @karthik_spain:好吧,我们不能这么说,因为我们需要研究具体案例。但是我们可以说是的,是“只是”一种构造代码的方式,由于上述原因,强制一个对象在整个系统中是“唯一的”。为了防止并发,有很多商品方法,恐怕单例不是其中之一:)
【解决方案3】:
  • 单例可以像全局变量一样使用。
  • 与普通类不同,它只能具有该类的一个实例(对象)。
  • 如果我们不想创建多个类的实例,例如数据库连接或实用程序库,我们会选择单例模式。
  • Singleton 确保您永远不会拥有多个类的实例。
  • 将构造方法设为私有以创建单例类。
  • 如果您不想实例化类的多个副本但只实例化一个,那么您只需将其置于单例模式中,您就可以调用该类的方法,并且该类在内存中也只有一个副本如果您创建它的另一个实例。
  • 如果用户只是想连接到数据库,那么如果该实例已经存在,则无需再次创建另一个实例,您可以使用第一个对象并建立连接。

例如

<?php
    class DBConn {

        private static $obj;

        private final function  __construct() {
            echo __CLASS__ . " initializes only once\n";
        }

        public static function getConn() {
            if(!isset(self::$obj)) {
                self::$obj = new DBConn();
            }
            return self::$obj;
        }
    }

    $obj1 = DBConn::getConn();
    $obj2 = DBConn::getConn();

    var_dump($obj1 == $obj2);
?>

输出:

DBConn initializes only once
bool(true)

Object1 和 Object2 将指向同一个实例

            _______________________
           |                       |
$obj1 ---->|  Instance of DBConn   |<----- $obj2
           |_______________________| 

希望有帮助!! :)

【讨论】:

    【解决方案4】:

    是的,你是对的“它不能被多次实例化”。当您打开数据库连接时,这个概念非常有用。

    假设,

    如果有人登录,则创建新的数据库对象。

    如果有人更新了一些东西,你就创建一个新的数据库对象。

    如果有人注销,你创建一个新的数据库对象。

    如您所见,每次与数据库交互时,都会创建一个新对象并打开一个新连接。就效率而言,这是一件坏事。

    这里的单例类确实解决了这个问题。它创建一个数据库连接对象并保存它,在需要时它只是返回它而不是创建一个新对象。

    class DB{
    private static $_instance = null;
    private $_pdo;
    
            private function __construct(){
                try{
                    $this->_pdo = new PDO('mysql:host ='yourhost'; dbname = 'your_database','username','password);
                    echo 'connected';
    
                }catch(PDOException $e){
                    die($e->getMessage());
                }
            }
          public static function getInstance(){
              if(!isset(self::$_instance)){
                  self::$_instance = new DB();
        }
        return self::$_instance; 
         }
      }
    

    如您所见,此类检查现有连接($_instance 属性),如果未设置则返回。

    【讨论】:

      猜你喜欢
      • 2010-11-02
      • 2011-01-10
      • 1970-01-01
      • 2020-08-05
      • 2018-01-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-12-13
      相关资源
      最近更新 更多