73

在 PHP 5 中,我可以重载构造函数(和任何其他方法)。但是,如果我得到这样的代码:

class Base {

    public function __construct($a, $b) {
        echo $a+$b;
    }


    public function sayHello() {
        echo 'Hello ';
    }
}


trait SayWorld {

    public function __construct($a, $b, $c = 0) {
        echo (int)$c * ($a+$b);
    }

    public function sayHello($a = null) {
        parent::sayHello();
        echo 'World!'.$a;
    }
}

class MyHelloWorld extends Base {
    use SayWorld;
}

$o = new MyHelloWorld(2, 3);
$o->sayHello(1);

我有一个错误:

致命错误:MyHelloWorld 具有来自特征的冲突构造函数定义

我该如何解决?你可以在这里测试我的代码。

4

3 回答 3

137

我认为现在做你想做的唯一方法是:

class MyHelloWorld extends Base {

    use SayWorld {
        SayWorld::__construct as private __swConstruct;
    }

    public function __construct($a, $b, $c = 0)
    {
        $this->__swConstruct($a, $b, $c);
    }
}

编辑2:

基于一年多来处理 PHP 中的特征,我的建议是:完全避免在特征中编写构造函数,或者如果必须 - 至少使它们无参数。将它们放在特征中违背了一般构造函数的想法,即:构造函数应该特定于它们所属的类。其他进化的高级语言甚至不支持隐式构造函数继承。这是因为构造函数与类的关系比其他方法强得多。事实上,它们之间的关系如此之强,以至于连LSP都不适用于它们。Scala 语言中的特征(Java 的一个非常成熟且对SOLID友好的继承者),不能有带参数的构造函数

编辑1:

PHP 5.4.11 中有一个错误,它实际上允许为超类方法设置别名。但是 PHP 开发人员认为这是一个禁忌,所以我们仍然坚持我上面介绍的那个繁琐的解决方案。但是那个错误引发了关于可以用它做什么的讨论,我希望它会在未来的版本中成为目标。

与此同时,我一遍又一遍地遇到同样的问题。我的愤怒随着 docblock 的参数和行数的增加而呈指数级增长,这些参数和行数必须重复很多次才能使用该特性。所以我想出了以下模式,以尽可能地遵守 DRY 规则:

而不是像这样重复整个参数集:

trait SayWorld {

    /**
     * This is a valid docblock.
     *
     * @param int $a Doc comment.
     * @param int $b Doc comment.
     */
    public function __construct($a, $b) {
        echo (int)$c * ($a+$b);
    }
}

class MyHelloWorld extends Base {

    use SayWorld {
        SayWorld::__construct as private __swConstruct;
    }

    /**
     * Repeated and unnecessary docblock.
     *
     * @param int $a Doc comment.
     * @param int $b Doc comment.
     * @param int $c Doc comment.
     */
    public function __construct($a, $b, $c = 0)
    {
        $this->__swConstruct($a, $b);
    }
}

我编写了一个类似于元组的类( C#Python用户熟悉的概念),并使用它来代替无穷无尽的参数列表:

class SayWorldConstructTuple
{
    public $a;

    public $b;

    public function __construct($a, $b)
    {
        $this->a = $a;
        $this->b = $b;
    }
}

class MyHelloWorld extends Base {

    use SayWorld {
        SayWorld::__construct as private __swConstruct;
    }

    /**
     * New and valid docblock.
     *
     * @param SayWorldConstructTuple $Tuple
     * @param int $c Additional parameter.
     */
    public function __construct(SayWorldConstructTuple $Tuple, $c = 0)
    {
        $this->__swConstruct($Tuple->a, $Tuple->b);
        $this->c = $c;
    }
}

注意:这种模式当然更适用于更多元组的构造函数参数,以及更多使用元组的类。

它可以通过使用 PHP 的动态特性进一步自动化。

于 2012-09-25T13:05:15.903 回答
7

尝试:

use SayWorld {
  Base::__construct insteadof SayWorld;
}

参考:PHP 文档

于 2012-09-18T13:39:36.080 回答
2

旧帖子,但万一这对任何人都有帮助:

我有类似的情况,但决定使用稍微不同的方法。我正在编写一个 WordPress 插件,想传递插件信息(版本、名称、文本域等),但不想在重构或扩展另一个类时更改每个文件,所以我用构造函数创建了一个 trait它只是为特定于类的操作调用一个 init 函数。

trait HasPluginInfoTrait{
    public function __construct() { 

        $this->plugin_name        = PLUGIN_NAME;
        $this->version            = PLUGIN_VERSION;

        if ( method_exists( $this, 'init' ){
            $this->init();
        }
    }
}

class SampleClass {
    use HasPluginInfoTrait;

    private function init(){
        // Code specific to SampleClass
    }
}
于 2019-07-22T19:48:00.970 回答