Лучшая практика, переопределение __construct () по сравнению с предоставлением метода init () - PullRequest
27 голосов
/ 21 ноября 2011

Когда вы создаете подклассы объектов и хотите расширить код инициализации, есть два подхода. Переопределение __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 ().

Ответы [ 5 ]

18 голосов
/ 21 ноября 2011

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

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

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

    // ...
  }
}

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

8 голосов
/ 21 ноября 2011

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

Кстати, вызов метода в подклассе из суперкласса называется Шаблонный метод .UseCase должен был бы организовать определенный рабочий процесс, но позволить подтипу влиять на его части.Это обычно делается обычными методами.Обратите внимание, что ваш конструктор не должен ничего организовывать, а просто инициализирует объект в допустимое состояние .

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

3 голосов
/ 21 ноября 2011

Во-первых

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

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

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

Редактировать

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

Это из Zend Docs:

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

1 голос
/ 21 ноября 2011

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

0 голосов
/ 21 ноября 2011

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

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

...