【问题标题】:What is the difference between a public constructor which calls a class method and a class method that calls another class method?调用类方法的公共构造函数和调用另一个类方法的类方法有什么区别?
【发布时间】:2020-02-19 19:42:17
【问题描述】:

我对 OO 编程有点陌生,无法理解为什么一种机制有效而另一种机制无效。

我创建了一个返回 MySQL 数据库句柄的简单类。我尝试直接从构造函数返回句柄失败。但是在创建实例后从类方法或类(?)方法成功。这是类定义和示例脚本

<?php

class HMMDatabaseHandle {

        private static $configfile = "config.json";

// uncomment for test 1
//      public function __construct () {
//              return self::get_handle_admin();
//      }

        public static function create() {
                return self::get_handle_admin();
        }

        private static function get_handle_admin() {
                $config = json_decode(file_get_contents(self::$configfile));
                $dbhost = $config->database->dbhost;
                $dbname = $config->database->dbname;
                $dbuser = $config->database->dbuser;
                $dbpass = $config->database->dbpass;

                try {
                        return new PDO("mysql:host=$dbhost;dbname=$dbname", $dbuser, $dbpass);
                }
                        catch(PDOException $e) {
                        echo $e->getMessage();
                }
        }
}

?>

这是我正在使用的测试脚本:

<?php
require_once 'HMMDatabaseHandle.php';

// Test 1 - fails (uncomment constructor func) at call to prepare() with:
// PHP Fatal error:  Call to undefined method HMMDatabaseHandle::prepare()
//$dbh = new HMMDatabaseHandle();

// Test 2 - works when class creates default constructor
// i.e. no explicit __construct() func
// Fetching data from executed query is fine
//$db = new HMMDatabaseHandle();
//$dbh = $db->create();

// Works using static class methods rather than instance
$dbh = HMMDatabaseHandle::create();

$sth = $dbh->prepare('select data_title,track_id from master');
$sth->execute();
while($row = $sth->fetch(PDO::FETCH_ASSOC)) {
   ...
}

我的问题是:

  1. 为什么我不能直接从构造函数返回句柄,因为它看起来与直接调用类方法非常相似?为什么构造函数调用类方法还是我的脚本调用它很重要?

  2. 如果我用 PHP 的默认构造函数创建一个实例,我真的是用 $db->create() 调用一个类方法吗?

我似乎在这里遗漏了一些基本概念。提前致谢!

【问题讨论】:

  • 类构造函数返回新创建的对象——没有别的。嗯,这并不完全正确。 new 关键字实际上返回了它,但构造函数通常与它一起使用,这使得构造函数似乎正在返回它。您可以将 __construct 函数视为在实例化类时执行某些代码的地方。它实际上没有返回任何内容/void。

标签: php class constructor class-method


【解决方案1】:

为什么我不能直接从构造函数返回句柄 好像和直接调用类方法这么像?

如果你能做到这一点,那么你就不会有HMMDatabaseHandle 的实例;你会有一个PDO 的实例。您将如何访问HMMDatabaseHandle 提供的任何其他方法?


虽然我完全同意@Don't Panic 的回答,但我还需要指出您正在混合使用静态方法和实例方法。

作为一般经验法则,当您希望能够在不先实例化对象的情况下调用方法时,请使用 static 关键字。如果你想实际创建和使用一个对象,那么你可以像这样定义你的类并使用$this-&gt;(或$object-&gt;,如果在类之外)而不是::来访问instance 属性和方法。

<?php
class HMMDatabaseHandle
{

    private $configfile = "config.json";


    public function __construct()
    {
        // You're not initializing anything in here, so this constructor is optional.
    }

    public function create()
    {
        return $this->get_handle_admin();
    }

    private function get_handle_admin()
    {
        $config = json_decode(file_get_contents($this->configfile));
        $dbhost = $config->database->dbhost;
        $dbname = $config->database->dbname;
        $dbuser = $config->database->dbuser;
        $dbpass = $config->database->dbpass;

        try {
            return new PDO("mysql:host=$dbhost;dbname=$dbname", $dbuser, $dbpass);
        }
        catch (PDOException $e) {
            echo $e->getMessage();
        }
    }
}

要实际执行此操作,您现在需要实例化新类。

$dbManager = new HMMDatabaseHandle();
$handle = $dbManager->create();

最后,您可以使用一个技巧来使您的构造函数可链接。只需将其括在括号中即可。

$handle = (new HMMDatabaseHandle())->create();

【讨论】:

【解决方案2】:

您不能在该上下文中从构造函数返回句柄,因为这会违反new 的定义行为。 new SomeClass(); 只会返回该类的一个实例,而不管在构造函数中调用了哪些其他方法。

__construct() 是一个 void 方法。 不打算返回任何东西1。这并不意味着其中的其他代码不会被执行,只是您的 return 在创建新对象的上下文中被忽略。这是有道理的,因为构造函数的主要目的是提供一种将依赖关系传递给对象的方法。有时它用于对对象进行额外的初始化/设置,但许多人认为除了将给定的参数分配给对象的属性之外,它不应该做任何工作。无论哪种方式,它都不需要返回任何东西。



1可以在你创建对象之后真正调用__construct()方法,然后它就会像普通方法一样工作你的return 就可以了。

$db = new HMMDatabaseHandle();
$dbh = $db->__construct();
var_dump($dbh);  // PDO Object

不过,这不是一件正常的事情,而且我想不出有什么用处或可取的场景。我只是想指出这是可能的。

【讨论】:

  • 啊!我现在明白了!谢谢!因此,例如,我可以从构造函数调用 get_handle_admin() 来设置实例变量,然后稍后使用实例方法获取它: $this->handle = self::get_handle_admin()
  • 不客气!是的,你可以这样做。不过,我建议阅读依赖注入。在内部创建自己的依赖项的类可能很难测试。
  • 很公平。您引用的依赖是数据库调用?是的,我一直在努力解决如何在这个 OOP 世界中抽象出与数据库的连接。关于这件事似乎有很多意见。 :-)
  • 我指的是 PDO 对象本身。如果您可以通过构造函数或使用 setter 将 PDO 对象分配给您的类,那么您将能够在测试期间模拟它。如果它是在类中创建的,那么任何测试都将依赖于与数据库的真实连接。
猜你喜欢
  • 1970-01-01
  • 2011-12-11
  • 1970-01-01
  • 1970-01-01
  • 2011-03-31
  • 1970-01-01
  • 2016-05-31
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多