web-dev-qa-db-ja.com

__construct()のオーバーライドとinit()メソッドの提供のベストプラクティス

オブジェクトをサブクラス化して初期化コードを拡張する場合、2つの方法があります。 __construct()をオーバーライドし、スーパークラスコンストラクターが呼び出す初期化メソッドを実装します。

方法1:

class foo
{
    public function __construct ($arg1, $arg2, $arg3)
    {
        // Do initialization
    }
}

class bar extends foo
{
    public function __construct ($arg1, $arg2, $arg3)
    {
        parent::__construct ($arg1, $arg2, $arg3);
        // Do subclass initialization
    }
}

方法2

class foo
{
    public function init ()
    {
        // Dummy function
    }

    public function __construct ($arg1, $arg2, $arg3)
    {
        // Do subclass defined initialization
        $this -> init ();
        // Do other initialization
    }
}

class bar extends foo
{
    public function init ()
    {
        // Do subclass initialization
    }
}

Zend Frameworkのドキュメントでは、コンストラクターのオーバーライドが推奨されていないようで、提供されている場合はinitメソッドをオーバーライドするように求められますが、これはどういうわけか私には正しくありません。 Zendはまた、私が満足できないいくつかのことを行う傾向があるため、ベストプラクティスの例として使用する必要があるかどうかはわかりません。私は個人的に最初のアプローチが正しいものだと思いますが、それが実際に私がやるべきことかどうか疑問に思うほど頻繁に2番目のアプローチを見てきました。

__constructのオーバーライドについてコメントはありますか?スーパークラスコンストラクターを呼び出すことを忘れないように注意する必要があることはわかっていますが、ほとんどのプログラマーはこれに注意する必要があります。

編集:私はZendを使用していません。__construct()をオーバーライドする代わりにinit()を使用するように促すコードベースの例としてのみ使用しています。

27
GordonM

2番目のアプローチが問題を延期しているようです。

クラスがある場合:

_class bar2 extends bar // which already extends foo
{
  public function init()
  {
    // You should then do anyway:
    parent::init();

    // ...
  }
}
_

parent::init()またはparent::__construct()の呼び出しを際限なく回避できなかったため、私も最初のアプローチをより論理的で簡単な方法で行います。最初のアプローチであるIMOは混乱が少ないです。

18
Frosty Z

init()を使用することが理にかなっていると考えられる唯一の2つの状況は、コンストラクターが非公開であるが、初期化に影響を与える機会を人々に与える必要がある場合です。抽象シングルトン(とにかく使いたくない)。または、Zend Frameworkのように、additional初期化を延期する必要がある場合(ただし、コンストラクタからinit()を呼び出さない)。

ちなみに、スーパークラスからサブクラスのメソッドを呼び出すことは テンプレートメソッド と呼ばれます。 UseCaseは、特定のワークフローを調整するものですが、サブタイプがワークフローの一部に影響を与えることを許可します。これは通常、通常の方法で行われます。コンストラクターは オブジェクトを有効な状態に初期化する 以外はオーケストレーションしないでください。

開発者がスーパータイプコンストラクターを呼び出すことを忘れないようにするために、コンストラクターからinit()を呼び出してしない必要があります。それは便利に聞こえるかもしれませんが、継承階層をすぐに台無しにしてしまいます。また、オブジェクトの通常の初期化方法とは異なり、開発者はスーパータイプのコンストラクターの呼び出しを学ぶ必要があるのと同じように、この新しい動作を学ぶ必要があることに注意してください。

8
Gordon

第一に

Zendはまた、私が満足していないいくつかのことをする傾向があります

使用しないことで、簡単に解決できます。

ただし、2番目に重要なのは、init()ではなく__construct()をオーバーライドすることです。init()はZendが使用するディスパッチ操作の一部であり、これを使用すると、アプリの残りの部分が確実に存在します。そして適所に。そうしないと、ZendのMVCモデルのフローが中断され、奇妙な動作になる可能性があります。

編集

私の主な理由は、他の開発者がいじるのを止めることです。 init()では何でもできますが、__construct()ではできません。これは、すべての正しいパラメーターを適切に使用して正しく実行する必要があるためです。

これはZend Docsからです:

アクションコントローラーのコンストラクターはいつでもオーバーライドできますが、これはお勧めしません。 Zend_Controller_Action :: _construct()は、リクエストおよびレスポンスオブジェクトの登録や、フロントコントローラから渡されたカスタム呼び出し引数など、いくつかの重要なタスクを実行します。コンストラクターをオーバーライドする必要がある場合は、必ずparent ::_ construct($ request、$ response、$ invokeArgs)を呼び出してください。

3
Jake N

コンストラクターをオーバーライドする場合、(通常)子クラスのコンストラクターの上部にある親コンストラクターを呼び出すことを忘れないようにする必要があるため、私はinit関数を使用します。あなたはそれに気づいているかもしれませんが、あなたのアプリケーションのメンテナンスを任された別の開発者がなることを保証することはできません。

1
MW.

init()の代わりに__construct()を使用するメリットの1つがわかります。コンストラクターでシグネチャが変更された場合、新しいシグネチャと一致するようにすべての派生クラスを更新する必要があります。

init()にパラメーターがない場合、これは起こりません。

0
Savageman