在PHP中执行多个构造函数的最佳方法

Jan*_*sen 329 php constructor multiple-constructors

您不能在PHP类中放置两个带有唯一参数签名的__construct函数.我想这样做:

class Student 
{
   protected $id;
   protected $name;
   // etc.

   public function __construct($id){
       $this->id = $id;
      // other members are still uninitialized
   }

   public function __construct($row_from_database){
       $this->id = $row_from_database->id;
       $this->name = $row_from_database->name;
       // etc.
   }
}
Run Code Online (Sandbox Code Playgroud)

在PHP中执行此操作的最佳方法是什么?

Kri*_*ris 454

我可能会这样做:

<?php

class Student
{
    public function __construct() {
        // allocate your stuff
    }

    public static function withID( $id ) {
        $instance = new self();
        $instance->loadByID( $id );
        return $instance;
    }

    public static function withRow( array $row ) {
        $instance = new self();
        $instance->fill( $row );
        return $instance;
    }

    protected function loadByID( $id ) {
        // do query
        $row = my_awesome_db_access_stuff( $id );
        $this->fill( $row );
    }

    protected function fill( array $row ) {
        // fill all properties from array
    }
}

?>
Run Code Online (Sandbox Code Playgroud)

然后,如果我想要一个我知道ID的学生:

$student = Student::withID( $id );
Run Code Online (Sandbox Code Playgroud)

或者,如果我有一个db行数组:

$student = Student::withRow( $row );
Run Code Online (Sandbox Code Playgroud)

从技术上讲,你不是构建多个构造函数,只是静态辅助方法,但是你可以通过这种方式避免构造函数中的大量意大利面条代码.

  • 从PHP 5.3开始,你应该使用`new static()`而不是`new self()`,因为`new static()`在子类中会更加明智. (10认同)
  • @gpilotino,矫枉过正,因为你需要另一个基本上只包含一个开关/案例决策树的类,(或方法),最后只是在两个方法中做了我已经做过的事情.在您无法轻松定义问题的确切约束(如创建表单元素)的情况下,工厂更有用.但那时,这只是我的意见和记录; 我并不认为这是事实. (6认同)
  • 我们难道也不能让`__construct()`私有,以防止有人偶尔分配一个"非初始化"实例? (4认同)
  • @mlvljr:你可以,但我建议保护它而不是私有.否则你很可能会遇到麻烦,如果你要扩展你的课程. (3认同)
  • 看起来你刚回答了我问gpilotino的问题.谢谢!非常清楚. (2认同)
  • 它解决了OP的问题.并且在PHP中重载(http://php.net/manual/it/language.oop5.overloading.php)_is_可能与你在c#或java等语言中的方式不同 (2认同)

tim*_*hew 82

Kris的解决方案非常好,但我发现工厂和流畅的风格更好:

<?php

class Student
{

    protected $firstName;
    protected $lastName;
    // etc.

    /**
     * Constructor
     */
    public function __construct() {
        // allocate your stuff
    }

    /**
     * Static constructor / factory
     */
    public static function create() {
        $instance = new self();
        return $instance;
    }

    /**
     * FirstName setter - fluent style
     */
    public function setFirstName( $firstName) {
        $this->firstName = $firstName;
        return $this;
    }

    /**
     * LastName setter - fluent style
     */
    public function setLastName( $lastName) {
        $this->lastName = $lastName;
        return $this;
    }

}

// create instance
$student= Student::create()->setFirstName("John")->setLastName("Doe");

// see result
var_dump($student);
?>
Run Code Online (Sandbox Code Playgroud)

  • 该代码存在一个重要问题:您无法确保实例有效(这就是存在构造函数的原因),并且通常不可变类更可取。 (5认同)
  • 正如有人习惯于编译的静态类型语言(如 C#),这种做事方式很适合我。 (4认同)
  • +1; 这种类型的解决方案可以产生非常好的代码。尽管我会在此解决方案中选择 `setLastName`(或者更确切地说是所有 setter)来返回 `$this`,而不是在同一属性上有效地使用两个 setter。 (2认同)

Daf*_*aff 41

PHP是一种动态语言,因此您无法重载方法.您必须检查您的参数类型,如下所示:

class Student 
{
   protected $id;
   protected $name;
   // etc.

   public function __construct($idOrRow){
    if(is_int($idOrRow))
    {
        $this->id = $idOrRow;
        // other members are still uninitialized
    }
    else if(is_array($idOrRow))
    {
       $this->id = $idOrRow->id;
       $this->name = $idOrRow->name;
       // etc.  
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 所有这一切都是令人敬畏的意大利面条代码.但它确实可能是最简单的方法. (20认同)
  • "动态"语言不排除函数/构造函数重载的可能性.它甚至不排除静态类型.即使如此,仍然有可能允许基于参数计数的重载.请不要以"动态"为借口. (12认同)

Bjö*_*örn 22

public function __construct() {
    $parameters = func_get_args();
    ...
}

$o = new MyClass('One', 'Two', 3);
Run Code Online (Sandbox Code Playgroud)

现在$ paramters将是一个值为'One','Two',3的数组.

编辑,

我可以补充一点

func_num_args()
Run Code Online (Sandbox Code Playgroud)

将为您提供该功能的参数数量.

  • 我想知道当一个新的开发人员被添加到一个包含大量代码的项目时会发生什么 (19认同)
  • 这是如何解决知道传递的问题的?我认为它使问题变得复杂,因为不必检查参数的类型,你必须检查是否设置了x参数,然后检查它的类型. (3认同)

Nas*_*jim 17

正如这里已经显示的那样,multiple在PHP 中声明构造函数的方法有很多种,但是没有correct一种方法可以这样做(因为PHP技术上不允许这样做).但它并没有阻止我们破解这个功能......这是另一个例子:

<?php

class myClass {
    public function __construct() {
        $get_arguments       = func_get_args();
        $number_of_arguments = func_num_args();

        if (method_exists($this, $method_name = '__construct'.$number_of_arguments)) {
            call_user_func_array(array($this, $method_name), $get_arguments);
        }
    }

    public function __construct1($argument1) {
        echo 'constructor with 1 parameter ' . $argument1 . "\n";
    }

    public function __construct2($argument1, $argument2) {
        echo 'constructor with 2 parameter ' . $argument1 . ' ' . $argument2 . "\n";
    }

    public function __construct3($argument1, $argument2, $argument3) {
        echo 'constructor with 3 parameter ' . $argument1 . ' ' . $argument2 . ' ' . $argument3 . "\n";
    }
}

$object1 = new myClass('BUET');
$object2 = new myClass('BUET', 'is');
$object3 = new myClass('BUET', 'is', 'Best.');
Run Code Online (Sandbox Code Playgroud)

来源: 使用和理解多个构造函数的最简单方法:

希望这可以帮助.:)


yan*_*nis 15

从版本5.4开始,PHP支持特征.这不是您正在寻找的,但基于特征的简单方法将是:

trait StudentTrait {
    protected $id;
    protected $name;

    final public function setId($id) {
        $this->id = $id;
        return $this;
    }

    final public function getId() { return $this->id; }

    final public function setName($name) {
        $this->name = $name; 
        return $this;
    }

    final public function getName() { return $this->name; }

}

class Student1 {
    use StudentTrait;

    final public function __construct($id) { $this->setId($id); }
}

class Student2 {
    use StudentTrait;

    final public function __construct($id, $name) { $this->setId($id)->setName($name); }
}
Run Code Online (Sandbox Code Playgroud)

我们最终得到两个类,每个构造函数一个,这有点适得其反.为了保持一定的理智,我会投入工厂:

class StudentFactory {
    static public function getStudent($id, $name = null) {
        return 
            is_null($name)
                ? new Student1($id)
                : new Student2($id, $name)
    }
}
Run Code Online (Sandbox Code Playgroud)

所以,这一切都归结为:

$student1 = StudentFactory::getStudent(1);
$student2 = StudentFactory::getStudent(1, "yannis");
Run Code Online (Sandbox Code Playgroud)

这是一种非常冗长的方法,但它可以非常方便.


And*_*c ॐ 13

你可以这样做:

public function __construct($param)
{
    if(is_int($param)) {
         $this->id = $param;
    } elseif(is_object($param)) {
     // do something else
    }
 }
Run Code Online (Sandbox Code Playgroud)


Jed*_*nch 8

这是一个优雅的方法来做到这一点。创建特征,在给定参数数量的情况下启用多个构造函数。您只需将参数数量添加到函数名称“__construct”。所以一个参数将是“__construct1”,两个“__construct2”......等等。

trait constructable
{
    public function __construct() 
    { 
        $a = func_get_args(); 
        $i = func_num_args(); 
        if (method_exists($this,$f='__construct'.$i)) { 
            call_user_func_array([$this,$f],$a); 
        } 
    } 
}

class a{
    use constructable;

    public $result;

    public function __construct1($a){
        $this->result = $a;
    }

    public function __construct2($a, $b){
        $this->result =  $a + $b;
    }
}

echo (new a(1))->result;    // 1
echo (new a(1,2))->result;  // 3
Run Code Online (Sandbox Code Playgroud)


roj*_*oca 7

另一种选择是像这样在构造函数中使用默认参数

class Student {

    private $id;
    private $name;
    //...

    public function __construct($id, $row=array()) {
        $this->id = $id;
        foreach($row as $key => $value) $this->$key = $value;
    }
}
Run Code Online (Sandbox Code Playgroud)

这意味着您需要使用这样的行实例化:$student = new Student($row['id'], $row)但要保持您的构造函数干净整洁。

另一方面,如果你想利用多态,那么你可以像这样创建两个类:

class Student {

    public function __construct($row) {
         foreach($row as $key => $value) $this->$key = $value;
    }
}

class EmptyStudent extends Student {

    public function __construct($id) {
        parent::__construct(array('id' => $id));
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 创建多个类来提供不同的构造函数确实是一个坏主意。“扩展”其他类的类应该扩展,这意味着它们应该添加功能,这就是 OOP 的重点,而不是这个。 (7认同)
  • 对我来说听起来像是经典的多态性,也就是所谓的面向对象编程。 (4认同)
  • 现在您有两个名称不同但功能相同的类,只是构造函数上的签名不同,这对我来说听起来很糟糕。 (2认同)