Подтвердить что ты не робот

Лучшая практика, переопределяющая __construct() по сравнению с предоставлением метода init()

Когда вы подклассифицируете объекты и хотите расширить код инициализации, существует два подхода. Переопределение __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? Я знаю, что вы должны быть осторожны, чтобы не забывать использовать конструктор суперкласса, но большинство программистов должны знать об этом.

EDIT: Я не использую Zend, я использую его только как пример кода, который побуждает вас использовать init() вместо переопределения __construct().

4b9b3361

Ответ 1

Похоже, второй подход откладывает проблему.

Если у вас есть класс:

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

    // ...
  }
}

Я бы пошел и на первый подход, более логичный и простой, поскольку вызов parent::init() или parent::__construct() не мог бесконечно избегать. Первый подход, ИМО, менее запутан.

Ответ 2

Единственные две ситуации, о которых я могу думать, в которых имеет смысл использовать init(), - это когда ваш конструктор является непубличным, но вам нужно дать людям возможность влиять на инициализацию, например. в абстрактном Singleton (который вы не хотите использовать в любом случае). Или, как и в Zend Framework, когда дополнительная инициализация должна быть отсрочена (но тогда вы не вызываете init() из конструктора).

Вызов метода в подклассе из суперкласса называется Template Method, кстати. UseCase должен был организовать определенный рабочий процесс, но разрешить подтип влиять на его части. Обычно это делается из обычных методов. Обратите внимание, что ваш конструктор не должен организовывать ничего, кроме инициализировать объект в допустимом состоянии.

Вы обязательно должны не вызывать/предлагать init() из конструктора, чтобы разработчики не забыли вызвать конструктор супертипа. Хотя это может показаться удобным, оно быстро испортит иерархию наследования. Также обратите внимание, что он отличается от того, как объекты обычно инициализируются, и разработчикам приходится изучать это новое поведение так же, как им нужно научиться называть конструктор супертипа.

Ответ 3

Во-первых,

Zend также имеет тенденцию делать несколько вещей, которые мне не нравятся

Вы можете решить это просто, не используя его.

Но во-вторых, и что более важно, вы должны переопределить init(), а не __construct(), потому что init() является частью операции отправки, которую использует и использует Zend, гарантирует, что остальная часть вашего приложения будет там и на месте. Выполнение в противном случае ломает поток модели Zend MVC и может привести к нечетному поведению.

Edit

Я думаю, что основной причиной для меня является то, что он останавливает других разработчиков от возиться. Вы можете делать что-либо с init(), но не с __construct(), так как это необходимо для правильной работы со всеми правильными параметрами.

Это из документов Zend:

Хотя вы всегда можете переопределить конструктор контроллера действий, мы не рекомендую это. Выполняется функция Zend_Controller_Action:: _ construct() некоторые важные задачи, такие как регистрация запроса и ответа объектов, а также любые пользовательские аргументы вызова, переданные из передний контроллер. Если вы должны переопределить конструктор, убедитесь, что call parent:: _ construct ($ request, $response, $invokeArgs).

Ответ 4

Я бы использовал функцию init, потому что если вы переопределяете конструктор, который вы (обычно) должны помнить, чтобы вызвать родительский конструктор в верхней части конструктора дочернего класса. Хотя вы можете быть в курсе этого, вы не можете гарантировать, что другой разработчик, которому поручено поддерживать ваше приложение, будет.

Ответ 5

Я вижу одно преимущество использования init() вместо __construct(): если подпись изменяется в конструкторе, вам нужно будет обновить каждый производный класс, чтобы он соответствовал новой сигнатуре.

Если init() не имеет параметра, этого не произойдет.