最佳实践,重写__construct()与提供init()方法

时间:2011-11-21 10:49:27

标签: php oop subclassing

当您继承对象并希望扩展初始化代码时,有两种方法。覆盖__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的文档似乎不鼓励重写构造函数,并希望您覆盖初始化方法(如果提供的话),但这对我来说感觉不对。 Zend还倾向于做一些我不满意的事情,所以我不确定它是否应该被用作最佳实践的一个例子。我个人认为第一种方法是正确的,但我已经看到第二种方法经常足以想知道这实际上是我应该做的。

您对覆盖__construct有何评论?我知道你必须小心记住调用超类构造函数,但大多数程序员应该知道这一点。

修改 我没有使用Zend,我只是将它作为代码库的一个例子,它鼓励你使用init()而不是覆盖__construct()。

5 个答案:

答案 0 :(得分:18)

看起来第二种方法是推迟问题。

如果您有课程:

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

    // ...
  }
}

我也会采用第一种方法,更合乎逻辑,更直接,因为无法无休止地避免parent::init()parent::__construct()调用。第一种方法,即IMO,不那么令人困惑。

答案 1 :(得分:8)

我能想到的唯一两种使用init()的情况是你的构造函数是非公开的,但你需要让人们有机会影响初始化,例如在一个抽象的Singleton(你不想使用它)。或者,与Zend Framework一样,附加初始化应该被推迟(但是你不要从构造函数中调用init())。

顺便说一句,从超类调用子类中的方法称为Template Method。 UseCase将协调某个工作流程,但允许子类型影响其中的部分工作流程。这通常是通过常规方法完成的。请注意,您的构造函数不应该只编排initialize the object into a valid state之外的任何内容。

你绝对应该从构造函数中调用/提供init(),以防止开发人员必须记住调用超类型构造函数。虽然这听起来很方便,但很快就会破坏继承层次结构。另请注意,它与通常初始化对象的方式不同,开发人员必须学习这种新行为,就像他们必须学习调用超类型的构造函数一样。

答案 2 :(得分:3)

首先

  

Zend还倾向于做一些我不满意的事情

您可以通过不使用它来简单地解决这个问题。

但其次,更重要的是,您应该覆盖init()而不是__construct(),因为init()是Zend使用和使用它的调度操作的一部分,可确保您的应用程序的其余部分存在并到位。否则会破坏Zend的MVC模型的流程,并可能导致奇怪的行为。

修改

我认为我的主要原因是它阻止其他开发者摆弄。您可以使用init()但不能使用__construct()执行任何操作,因为这需要正确运行所有正确的参数。

这是来自Zend Docs:

  

虽然你总是可以覆盖动作控制器的构造函数,但是我们   不推荐这个。 Zend_Controller_Action :: _ construct()执行   一些重要的任务,例如注册请求和响应   对象,以及从中传入的任何自定义调用参数   前控制器。如果必须覆盖构造函数,请务必   call parent :: _construct($ request,$ response,$ invokeArgs)。

答案 3 :(得分:1)

我会使用init函数,因为如果你覆盖构造函数,你(通常)必须记住在子类的构造函数的顶部调用父构造函数。虽然您可能已经意识到这一点,但您无法保证负责管理您的应用程序的其他开发人员将会这样做。

答案 4 :(得分:0)

我可以看到使用init()而不是__construct()的一个好处:如果签名在构造函数中发生更改,则必须更新每个派生类以匹配新签名。

如果init()没有参数,则不会发生这种情况。