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でトレイトを扱ってきた1年以上に基づく私のアドバイスは、次のとおりです。トレイトでコンストラクターを作成することは絶対に避けてください。必要な場合は、少なくともパラメーターを使用しないようにしてください。それらを特性に含めることは、一般的なコンストラクターの概念に反します。つまり、コンストラクターは、それらが属するクラスに固有である必要があります。他の進化した高級言語は、暗黙のコンストラクター継承さえサポートしていません。これは、コンストラクターが他のメソッドよりもクラスとの関係がはるかに強いためです。実際、それらは非常に強い関係を持っているので、 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プラグインを書いていて、プラグイン情報(バージョン、名前、テキストドメインなど)を渡したいと思っていましたが、別のクラスをリファクタリングまたは拡張するときに各ファイルを変更したくなかったので、コンストラクターで特性を作成しましたこれは、クラス固有の操作のために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 に答える