为啥我不能在 PHP 中重载构造函数?

Posted

技术标签:

【中文标题】为啥我不能在 PHP 中重载构造函数?【英文标题】:Why can't I overload constructors in PHP?为什么我不能在 PHP 中重载构造函数? 【发布时间】:2011-01-11 06:49:53 【问题描述】:

我已经放弃了能够在 php 中重载我的构造函数的所有希望,所以我真正想知道的是为什么

这有什么原因吗?它会创建本质上糟糕的代码吗?是被广泛接受的语言设计不允许它,还是其他语言比 PHP 更好?

【问题讨论】:

有一件事是确定的。缺乏显式的构造函数重载,就像在 Java 或 C++ 意义上的那样,让你认真思考参数列表中真正应该包含的内容(鉴于许多人会使用函数参数的默认值来克服这个问题)。 :-) 依赖注入不是重载构造函数吗? 【参考方案1】:

您不能在 PHP 中重载任何方法。如果您希望能够在传递几种不同的参数组合时实例化 PHP 对象,请使用带有私有构造函数的工厂模式。

例如:

public MyClass 
    private function __construct() 
    ...
    

    public static function makeNewWithParameterA($paramA) 
        $obj = new MyClass(); 
        // other initialization
        return $obj;
    

    public static function makeNewWithParametersBandC($paramB, $paramC) 
        $obj = new MyClass(); 
        // other initialization
        return $obj;
    


$myObject = MyClass::makeNewWithParameterA("foo");
$anotherObject = MyClass::makeNewWithParametersBandC("bar", 3);

【讨论】:

+1 为了保持整洁,这种方法清楚地说明了你想要达到的目标,对我来说更干净。 这不是违反单一职责原则吗?您正在创建您的类(一项职责),并且您还正在执行各种类功能(您的主要类职责)。在实际场景中,您可能需要从外部来源(即数据库)读取一些 $parameters,这会产生另一个责任。 我是 PHP 新手——已经完成了大量的 Java:HOLY 限制蝙蝠侠!您的示例甚至不遵循工厂模式。 en.wikipedia.org/wiki/Factory_method_pattern @Chrisian 这是一个静态工厂方法。有关工厂的更多一般信息,请参阅Factory (object-oriented programming)。它们实际上是创建对象而不是使用 new 的任何东西。 正如@J.D.Sandifer 所说,这是一家工厂。如果有人不喜欢干净代码的“静态工厂”,您总是可以创建一个具有makeNewWithParameterA()makeNewWithParametersBandC()MyClassFactory 对象而不是静态的,因此它们更适合“依赖注入”框架。但是,如果您控制环境并且不想创建“工厂类”,这是最干净的方式,正如@SamGiles 所说。使用相同构造函数和 switch-case 来调用私有方法的解决方案是糟糕的代码。【参考方案2】:

您可以使用variable arguments 来产生相同的效果。如果没有强类型,添加默认参数和所有其他“变通方法”就没有多大意义。

【讨论】:

这并不总是可能的,例如,如果我希望一个重载是公共的,而另一个重载是私有的。【参考方案3】:

为了完整起见,我建议Fluent Interfaces。这个想法是,通过将return $this; 添加到方法的末尾,您可以将调用链接在一起。所以不是

$car1 = new Car('blue', 'RWD');
$car2 = new Car('Ford', '300hp');

(这根本行不通),你可以这样做:

$car = (new Car)
       ->setColor('blue')
       ->setMake('Ford')
       ->setDrive('FWD');

这样您就可以准确地选择要设置的属性。在很多方面,它类似于将一组选项传递给您的初始调用:

$car = new Car(['make' => 'Ford', 'seats' => 5]);

【讨论】:

嗯,我不得不说这是我最喜欢的方式,非常可读和灵活 但它不是不可变的,你可以用无效状态实例化类。 这打破了 ValueObjects 背后不应该有 setter 的想法。 另一个问题是您不确定是否设置了所有需要设置的变量。其次,不会引发编译时错误,而是您必须依赖可能会发生也可能不会发生的运行时错误,具体取决于变量的使用情况。 如果一个人打算在依赖注入器中使用一个类,这是一种建立可以使用的初始状态的形式吗?另外,当您正在实例化的对象需要将值传递给超类/父类构造函数时会发生什么?【参考方案4】:

PHP 确实不支持真正的重载。正如@Pestilence 提到的,您可以使用可变参数。有些人只是使用各种选项的关联数组来克服这个问题。

【讨论】:

【参考方案5】:

PHP Manual: Function Arguments, Default Values

我只是通过使用函数参数的默认值来克服这个问题。在__constuct 中,首先列出所需的参数。后面的可选参数以$param = null的一般形式列出。

class User

    private $db;
    private $userInput;

    public function __construct(Database $db, array $userInput = null)
    
        $this->db = $db;
        $this->userInput = $userInput;
    

这可以被实例化为:

$user = new User($db)

$user = new User($db, $inputArray);

这不是一个完美的解决方案,但我通过将参数分成绝对强制参数来完成这项工作,无论何时构造对象,并且作为一个组, 可选参数按重要性顺序列出

有效。

【讨论】:

【参考方案6】:

他们说这项工作:

<?php
class A

    function __construct()
    
        $a = func_get_args();
        $i = func_num_args();
        if (method_exists($this,$f='__construct'.$i)) 
            call_user_func_array(array($this,$f),$a);
        
    

    function __construct1($a1)
    
        echo('__construct with 1 param called: '.$a1.PHP_EOL);
    

    function __construct2($a1,$a2)
    
        echo('__construct with 2 params called: '.$a1.','.$a2.PHP_EOL);
    

    function __construct3($a1,$a2,$a3)
    
        echo('__construct with 3 params called: '.$a1.','.$a2.','.$a3.PHP_EOL);
    

$o = new A('sheep');
$o = new A('sheep','cat');
$o = new A('sheep','cat','dog');

// results:
// __construct with 1 param called: sheep
// __construct with 2 params called: sheep,cat
// __construct with 3 params called: sheep,cat,dog
?>

而且,似乎每个人都对此感到满意,但对我来说,它没有用...... 如果你让它工作,它也是一种重载......

它接受所有参数并将它们传递给辅助函数构造函数...

【讨论】:

会尝试这个的人,不要打扰。它不会起作用,__construct 不扩展默认构造函数,并且与现在类中的任何其他自定义函数基本相同 那么这如何与类型提示一起工作?现在只看变量的数量 @Mazzy 自 2012 年以来我没有编写任何 PHP 代码,所以恐怕我应该说,类型提示并没有在我脑海中敲响警钟【参考方案7】:
<?php
//php do not automatically call parent class constructor at all if child class has constructor so you have to call parent class constructor explicitly, however parent class constructor is called automatically if child class has no constructor
class MyClass 

    function construct1($value1)
    
        echo "<br/> dummy constructor is called with 1 arguments and it is $value1";
    
    function construct2($value1,$value2)
    
        echo "<br/> dummy constructor is called with 2 arguments and it is $value1, $value2";
    
    function construct3($value1,$value2,$value3)
    
        echo "<br/> dummy constructor is called with 3 arguments and it is $value1, $value2 , $value3";
     
    public function __construct()
    
        $NoOfArguments = func_num_args(); //return no of arguments passed in function
        $arguments = func_get_args();
        echo "<br/> child constructor is called $NoOfArguments";
        switch ($NoOfArguments) 
            case 1:
                 self::construct1($arguments[0]);
                break;
            case 2:
                self::construct2($arguments[0],$arguments[1]);
                break;

            case 3:
                self::construct3($arguments[0],$arguments[1],$arguments[2]);
                break;

            default:
                echo "Invalid No of arguments passed";
                break;
        
    



$c = new MyClass();
$c2 = new MyClass("ankit");
$c2 = new MyClass("ankit","Jiya");
$c2 = new MyClass("ankit","Jiya","Kasish");

?>

【讨论】:

【参考方案8】:

您可以在构造函数中使用条件语句,然后执行您的任务。 例如。

  class Example
  
      function __construct($no_of_args)

      // lets assume 2
          switch($no_of_args)
          
              case 1:
                // write your code
              break;
              case 2:
                //write your 2nd set of code
              break;
              default:
           //write your default statement
         
      
   

    $object1 = new Example(1);  // this will run your 1st case
    $object2 = new Example(2);  // this will run your 2nd case

等等……

【讨论】:

【参考方案9】:

您当然可以使用 __call() 和 __callStatic() 魔术方法重载 PHP 中的任何函数。 这有点棘手,但实现可以完全满足您的需求。 以下是 PHP.net 官方网站上的资源:

https://www.php.net/manual/en/language.oop5.overloading.php#object.call

以下是适用于静态和非静态方法的示例:

class MethodTest

    public function __call($name, $arguments)
    
        // Note: value of $name is case sensitive.
        echo "Calling object method '$name' "
             . implode(', ', $arguments). "\n";
    

    /**  As of PHP 5.3.0  */
    public static function __callStatic($name, $arguments)
    
        // Note: value of $name is case sensitive.
        echo "Calling static method '$name' "
             . implode(', ', $arguments). "\n";
    


$obj = new MethodTest;
$obj->runTest('in object context');

MethodTest::runTest('in static context');  // As of PHP 5.3.0

您可以通过在 __construct() 中使用以下代码将其应用于构造函数:

$clsName = get_class($this);
$clsName->methodName($args);

很简单。 您可能希望实现 __clone() 以使用您调用的方法制作类的克隆副本,而无需在每个实例中调用的函数...

【讨论】:

【参考方案10】:

据我所知,PHP 中的构造函数重载是不允许的,因为 PHP 的开发人员没有包含该功能 - 这是对 PHP 的众多抱怨之一。

我听说过技巧和变通方法,但是缺少 OOP 意义上的真正重载。也许在未来的版本中,它会被包含在内。

【讨论】:

【参考方案11】:

我认为我们也可以使用 带有默认参数的构造函数作为 PHP 中构造函数重载的潜在替代品。

不过,PHP 不支持真正的构造函数重载真的很遗憾。

【讨论】:

【参考方案12】:
<?php

    class myClass 

        public $param1 = 'a';
        public $param2 = 'b';

        public function __construct($param1 = NULL, $param2 = NULL) 

            if ($param1 == NULL && $param2 == NULL) 
//                $this->param1 = $param1;
//                $this->param2 = $param2;
             elseif ($param1 == NULL && $param2 !== NULL) 
//                $this->param1 = $param1;
                $this->param2 = $param2;
             elseif ($param1 !== NULL && $param2 == NULL) 
                $this->param1 = $param1;
//                $this->param2 = $param2;                
             else 
                $this->param1 = $param1;
                $this->param2 = $param2;
            

        

    

//    $myObject  = new myClass();
//    $myObject  = new myClass(NULL, 2);
    $myObject  = new myClass(1, '');
//    $myObject  = new myClass(1, 2);

    echo $myObject->param1;
    echo "<br />";
    echo $myObject->param2;

?>

【讨论】:

【参考方案13】:

添加这个答案是为了相对于当前 PHP 的完整性,因为 PHP 的更高版本,您实际上可以以某种方式重载构造函数。以下代码将有助于理解,

<?php
class A

    function __construct()
    
        $a = func_get_args();
        $i = func_num_args();
        if (method_exists($this,$f='__construct'.$i)) 
            call_user_func_array(array($this,$f),$a);
        
    
   
    function __construct1($a1)
    
        echo('__construct with 1 param called: '.$a1.PHP_EOL);
    
   
    function __construct2($a1,$a2)
    
        echo('__construct with 2 params called: '.$a1.','.$a2.PHP_EOL);
    
   
    function __construct3($a1,$a2,$a3)
    
        echo('__construct with 3 params called: '.$a1.','.$a2.','.$a3.PHP_EOL);
    

$o = new A('sheep');
$o = new A('sheep','cat');
$o = new A('sheep','cat','dog');
?>

输出:

__construct with 1 param called: sheep
__construct with 2 params called: sheep,cat
__construct with 3 params called: sheep,cat,dog

【讨论】:

【参考方案14】:
 public function construct1($user , $company)

    dd("constructor 1");
    $this->user = $user;
    $this->company = $company;


public function construct2($cc_mail , $bcc_mail , $mail_data,$user,$company)

    dd('constructor 2');
    $this->mail_data=$mail_data;
    $this->user=$user;
    $this->company=$company;
    $this->cc_mail=$cc_mail;
    $this->bcc_mail=$bcc_mail;


public function __construct()

    $NoOfArguments = func_num_args(); //return no of arguments passed in function
    $arguments = func_get_args();
    switch ($NoOfArguments) 
        case 1:
             self::construct1($arguments[0]);
            break;
        case 5:
            self::construct2($arguments[0],$arguments[1],$arguments[2],$arguments[3],$arguments[4]);
            break;
        default:
            echo "Invalid No of arguments passed";
            break;
    

【讨论】:

【参考方案15】:

在这种情况下,我建议使用接口:

interface IExample 

    public function someMethod();



class oneParamConstructor implements IExample 

    public function __construct(private int $someNumber) 

    

    public function someMethod()

    


class twoParamConstructor implements IExample 

    public function __construct(private int $someNumber, string $someString) 

    

    public function someMethod()

    

比你的代码:

function doSomething(IExample $example) 

    $example->someMethod();



$a = new oneParamConstructor(12);
$b = new twoParamConstructor(45, "foo");

doSomething($a)
doSomething($b)

【讨论】:

【参考方案16】:

我真的不是 OOP 专家,但据我了解 重载 意味着方法能够根据其作为输入接收的参数采取不同的行动。这在 PHP 中是很有可能的,你只是不声明输入类型,因为 PHP 没有强类型,所有的重载都是在运行时而不是编译时完成的。

【讨论】:

实际上,重载意味着根据参数的数量(和类型,对于强类型语言)运行不同的函数。这与具有相同功能的不同行为不同。例如,我现在正在编辑一个由听说过 OOP 但不知道如何使用它的人创建的类。我想创建一个 my 代码将调用的构造函数,但我不想破坏她的工作代码。

以上是关于为啥我不能在 PHP 中重载构造函数?的主要内容,如果未能解决你的问题,请参考以下文章

为啥在父类的构造函数中调用重载函数时,在 ES6 类上设置属性不起作用

为啥我能够为 QObject 子类创建复制构造函数并重载赋值运算符?

如何在 PHP >= 5.4 的特征中重载类构造函数

如果我在 Scala 中定义多个重载构造函数,我不能定义默认值吗?

PHP中的构造函数重载

golang函数中的参数为啥不支持默认值