当您继承对象并希望扩展初始化代码时,有两种方法。覆盖__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()。
答案 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()
没有参数,则不会发生这种情况。