【问题标题】:Creating singleton - is this really right?创建单例 - 这真的正确吗?
【发布时间】:2011-02-14 22:18:09
【问题描述】:

我正在尝试创建自己的轻量级但 OOP 方式来跟踪在我的 PHP 框架中加载的文件及其适当的版本(尚未实现),以便将来能够轻松地测试依赖关系问题。

这是我当前的代码,但作为 OOP 和模式的新手,我无法确定我是否完成了正确的单例:

class loadRegistry {

    private static $registry = null;

    private function __construct(){

        if (is_null(self::$registry));
        self::$registry = array();

    }

    public static function get() {
        return self::$registry;
    }

    public static function set($filename){

        if ( isSet( self::$registry[$filename]) AND !empty($filename) ) {
            throw new Exception("File already loaded");
        }
        else self::$registry[$filename] = '';
    }

}

loadRegistry::set('filename');
loadRegistry::set('filename2');

$reg = loadRegistry::get();

【问题讨论】:

  • 那不是单例。我不确定它想成为什么......它几乎看起来像一个抽象工厂,但我看不出在哪里处理对象。更不用说你可以只做private static $registry = array() 并保存 is_null 检查......而且你无法实例化它,所以无论如何都没有注册表......
  • (related) Why Singletons have no use in PHP 是我对Who needs Singletons的回答的较长版本
  • @Gordon:非常感谢您的提醒!
  • 不客气。 @middaparka 提供的链接也不错。

标签: php oop design-patterns singleton


【解决方案1】:

不,它不是单例。您实际上只是在玩静态属性。单身人士会更像这样:

class Registry {

    private static $registry = null;
    private $data;

    private function __construct(){
        $this->data = array();
    }

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

    public function set($filename){
        if (isset($this->data[$filename]) && !empty($filename) ) {
            throw new Exception("File already loaded");
        }
        else {
            $this->data[$filename] = '';
        }
    }

    // further **instance** methods
}

$reg = Registry::getInstance();
$reg->set('filename');
$reg->set('filename2');

不知道能不能在PHP中设置构造函数为private...我觉得不行。

静态类$ 和单例之间是有区别的。单例允许一次只有一个实例。对于静态类,您甚至不需要实例。此外,静态属性在类的所有实例之间共享(如果您创建实例)。

如果您不需要实例来保持某些状态,您通常会使用静态类。

$:没有真正的静态类。只有成员可以定义为静态的。我会说只有静态成员的类可以称为静态类。

【讨论】:

  • 设置函数不应该是静态的。
  • 私有构造函数在尝试实例化时会解析并导致致命错误。
  • @webbie:这就是你想要的,因为单例的全部意义在于防止从类本身外部实例化(所以getInstance 是实例化类的唯一方法)......构造函数应该是private function __construct,否则就不是单例了……
  • @ircmaxwell:我只是在为 Felix 详细回答。
  • 对于真正的单例,您还应该覆盖 __clone() 函数。
【解决方案2】:

您在上面介绍的内容没有遵循单例模式。 Singleton 一次只允许该类的一个实例存在(与定义静态对象相反,这不会阻止您同时创建其他对象并将它们混合在一起)。

您可以在官方手册中阅读 PHP 中的单例模式; http://php.net/manual/en/language.oop5.patterns.php

上面的手动示例给出了一个准系统示例,但自从 PHP5 发布以来,我从未真正需要在 PHP 中使用 Singleton。它们在 iPhone 等设备上更有用,在这些设备上,您只需要一个接口来连接给定的硬件——比如屏幕。但是,我想您可能会将它用于 PHP 应用程序中的数据库连接...

【讨论】:

    【解决方案3】:

    这段代码看起来很奇怪——首先,构造函数永远不会被任何内部函数调用,也不能被外部调用,因为它是私有的。它不是单例,因为单例实际上是作为对象创建的,而这个类从来都不是。

    但是,如果您想这样做,您可以使用这样的代码来创建分隔的全局存储。

    这是给你的单身人士:

    class Singleton {
       private static $me;
    
       private function __construct 
       { 
           /* something */ 
       }
    
       public static getInstance() 
       {
          if(empty(self::$me)) self::$me = new self;
          return self::$me;
       }
    

    }

    这是改进的 5.3+ 单例:

    class Singleton {
       private static $me;
    
       private function __construct 
       { 
          /* something */ 
       }
    
       public static getInstance() 
       {
          if(empty(self::$me)) self::$me = new static;
          return self::$me;
       }
    

    }

    【讨论】:

    • 不保证单实例。可以克隆和序列化。
    • 好的,如果重要的话,您可以为此添加私有 __clone 和 __sleep 和 __wakeup。
    【解决方案4】:

    不过我觉得这有点老了..

    我以前只是为了单实例而使用单例,但是有了注册表,您就不再需要静态了。 另一方面是如何实现和使用注册表,这将决定注册表解决方案是否良好。

    因为我使用 MVC,并且只需要为基本控制器和基本模型传递一次注册表,仅此而已。另一方面,如果一个设计需要经常传递注册表对象,那可能不是一个好主意。

    基本思路是:

    1. 有一个注册表

    2. 一开始只创建一个数据库类的实例,这样我们就不用费心创建连接等。我使用两个数据库,直到第一次需要它时才创建连接。

      $registry->dbManager = new dbManager();

    3. 将注册表对象传递给路由器、控制器和模型基类注册表随处可见。
      $registry = My_Registry::singleton(); $registry->router = 新路由器($registry); $registry->dbManager = new dbManager(); 等等…………

    4. db 类保持普通类,这只是 db 类的一部分

      类 dbManager {

      //DB Connections
      private  $Internal_db_con = null;
      private  $Business_db_con = null;
      
      //DB Connection Parameters
      private  $host=null;
      private  $user=null;
      private  $pass=null;
      
      //DB Names
      private  $internal_db_name=null;
      private  $business_db_name=null;    
      
      //
      private $result = null;
      
      
      
      //   only one object created
      public  function __construct()
      { 
              include_once __SITE_PATH . 'includes/db_config.inc';
      

      list($this->host,$this->user, $this->pass, $this->internal_db_name,$this->business_db_name ) = $db_conf;

      }
      
      
      
      
      //set  internal database connection
      private function setInternalDBCon()
      { debug('setInternalDBCon called');
                  try{    
                      # MySQL with PDO_MYSQL  
                      $dbq="\"";
                      $this->Internal_db_con = new 
      

      PDO("mysql:host=".$this->host.";dbname=".$this->internal_db_name ,$this->user, $this->pass, 数组(PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8"));

      $this->Internal_db_con->setAttribute (PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
                      }  
                  catch(PDOException $e) 
                  {  
                  echo 'Error connecting to MySQL: ' .$e->getMessage();  
                  }    
      
      }
      
      
      
      
      
      
      //set  business database connection
      public  function setBusinessDBCon()
      { 
                  try{    
                  # MySQL with PDO_MYSQL  
                  $dbq="\"";
                  $this->Business_db_con = new 
      

      PDO("mysql:host=".$this->host.";dbname=".$this->internal_db_name ,$this->user, $this->pass, 数组(PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8"));

      $this->Business_db_con->setAttribute (PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); }
      捕获(PDOException $e) {
      echo '连接 MySQL 时出错:' .$e->getMessage();
      }

      }
      
      
      
      
      
      
      /*check if a row exists in a safe way that avoids sql atack*/
      public  function uniqueRowExists($dbcon, $table,$col_name, $data)
      {    
              //check if connection is set 
              if($this->{$dbcon.'_db_con'}==null) //dynamic variable call
          $this->callMyFunc( 'set'.$dbcon.'DBCon');  //dynamic function call
      

      .............等等......

              //prepare
              $stmt =  $this->{$dbcon.'_db_con'}->prepare($sql);
          $stmt->execute($data);
      

      ........等等

    5. 这就是我从 db 类调用公共函数的方式

      //查询数据库
      $result=$this->registry->dbManager->uniqueRowExists ('Internal', 'internal_user', Array('uname','pass'), $input); //可供选择的连接,要搜索的表、列、数组数据

    这样,任何地方都只有一个实例。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-07-06
      • 2020-01-17
      • 1970-01-01
      • 2019-06-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-12-02
      相关资源
      最近更新 更多