Лучшая практика, переопределяющая __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 ().

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

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

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

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

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

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

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

в первую очередь

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

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

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

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

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

Это из Zend Docs:

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

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

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

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