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

2022-08-30 15:43:12

当您对对象进行子类化并希望扩展初始化代码时,有两种方法。重写__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也倾向于做一些我不满意的事情,所以我不确定它是否应该被用作最佳实践的一个例子。我个人认为第一种方法是正确的,但我经常看到第二种方法,以至于想知道这是否真的是我应该做的。

你对覆盖__construct有什么意见吗?我知道你必须小心记住调用超类构造函数,但大多数程序员应该意识到这一点。

编辑:我没有使用Zend,我只是将其用作代码库的一个示例,鼓励您使用init()而不是覆盖__construct()。


答案 1

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

如果您有课程:

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

    // ...
  }
}

我也会选择第一种方法,更合乎逻辑和直接,因为or呼叫无法无休止地避免。第一种方法,IMO,不那么令人困惑。parent::init()parent::__construct()


答案 2

我能想到的只有两种情况是,当你的构造函数是非公开的,但你需要给人们一个机会来影响初始化,例如在抽象的单例中(你无论如何都不想使用它)。或者,就像在Zend Framework中一样,当额外的初始化应该推迟时(但随后你不会从构造函数调用)。init()init()

顺便说一句,从超类调用子类中的方法称为模板方法。用例是编排某个工作流,但允许子类型影响其部分内容。不过,这通常是通过常规方法完成的。请注意,构造函数不应编排任何内容,而只需将对象初始化为有效状态即可。

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


推荐