Почему бы не создать экземпляр нового объекта внутри конструктора объекта?

Я ответил на вопрос ( ссылку ), что я использовал создание нового объекта в конструкторе другого класса, вот пример:

class Person { public $mother_language; function __construct(){ // just to initialize $mother_language $this->mother_language = new Language('English'); } 

И я получил комментарий от пользователя «Matija» ( его профиль ), и он написал: « Вы никогда не должны создавать экземпляр нового объекта внутри объекта consturctor, зависимости должны быть вытеснены извне, поэтому каждый, кто использует этот класс, знает, на что этот класс зависит!

В общем, я могу согласиться с этим, и я понимаю его точку зрения.

Однако я часто делал это так, например:

  • поскольку частные свойства других классов дают мне функциональность, которую я могу решить, не дублируя код, например, я могу создать список (класс, реализующий интерфейс ArrayAccess ) объектов), и этот класс будет использоваться в другом классе, который имеет такой список объектов,
  • некоторые классы используют, например, объекты DateTime ,
  • если я include (или автоматически загружаю) зависимый класс, то не должно быть проблем с ошибками,
  • потому что зависимые объекты могут быть очень большим числом, передавая их все в конструктор класса, может быть очень длинным и непонятным, например

     $color = new TColor('red'); // do we really need these lines? $vin_number = new TVinNumber('xxx'); $production_date = new TDate(...); ... $my_car = new TCar($color, $vin_number, $production_date, ...............); 
  • поскольку я был «рожден» в Паскале, а затем в Дельфи, у меня есть некоторые привычки оттуда. И в Delphi (и FreePascal как его конкурент) эта практика очень часто. Например, существует класс TStrings который обрабатывает массив строк, и для их хранения он не использует array s, а другой класс TList , который предоставляет некоторые полезные методы, в то время как TStrings – это только некоторый вид интерфейса. Объект TList является объявленным пользователем и не имеет доступа извне, но получатели и сеттеры TStrings .

  • (не важно, но по какой-то причине), как правило, я тот, кто использует мои классы.

Пожалуйста, объясните мне, действительно ли важно избегать создания объектов в конструкторах?

Я прочитал эту дискуссию, но до сих пор неясно.

Related of "Почему бы не создать экземпляр нового объекта внутри конструктора объекта?"

Да, это действительно так. Затем вы четко понимаете, что нужно для создания объекта. Большое количество зависимых объектов, передаваемых внутри, является запахом кода, который, возможно, ваш класс делает слишком много и должен быть разбит на несколько меньших классов.

Главное преимущество передачи в зависимых объектах происходит, если вы хотите протестировать свой код. В вашем примере я не могу использовать поддельный класс Language. Я должен использовать фактический класс для проверки Person. Теперь я не могу контролировать, как язык ведет себя, чтобы убедиться, что Person работает правильно.

Этот пост помогает объяснить, почему это плохо и потенциальные проблемы, которые он вызывает. http://misko.hevery.com/code-reviewers-guide/flaw-constructor-does-real-work/

ОБНОВИТЬ

Тестирование в сторону, передача в зависимые объекты также делает ваш код более явным, гибким и расширяемым. Чтобы процитировать сообщение в блоге, которое я связал с:

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

В вашем примере вы можете создавать людей, которые имеют «английский» как язык. Но как насчет того, когда вы хотите создать кого-то, кто говорит «французский». Я не могу это определить.

Что касается создания объектов и их передачи, то это целая цель шаблона Factory http://www.oodesign.com/factory-pattern.html . Он создавал бы зависимости и вводил бы их для вас. Таким образом, вы сможете запросить его для объекта, который будет инициализироваться так, как вы хотите. Объекту Person не нужно решать, что ему нужно.

Оригинальный комментарий кажется мне глупым.

Нет причин бояться классов, которые управляют ресурсами . Действительно, инкапсуляция, которую предоставляют классы, идеально подходит для этой задачи.

Если вы ограничиваете себя только тем, что нажимаете ресурсы извне, то что будет управлять этими ресурсами? Процедурный код спагетти? Хлоп!

Старайтесь не верить всему, что вы читаете в Интернете.

Это скорее проблема проверки, чем правильная / неправильная вещь, если вы создаете объект на своем конструкторе, вы никогда не сможете самостоятельно тестировать свой класс.

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

Таким образом, вы сможете свободно издеваться над любым из тех инъецированных объектов, чтобы действительно протестировать свой собственный класс.

 <?php class Person { public $mother_language; // We ask for a known interface we don't mind the implementation function __construct(Language_Interface $mother_language) { $this->mother_language = $mother_language } static function factory() { return new Person(new Language('English')); } } $person = Person::factory(); $person = new Person(new Language('Spanish'); // Here you are free to inject your mocked object implementing the Language_Interface 

Я думаю, это зависит от ситуации. Если вы посмотрите, например, на Doctrine 2, рекомендуется, чтобы ваши значения всегда были равны null и задавали ваши значения в конструкторе. Это потому, что Doctrine 2 пропускает экземпляр класса, когда он извлекается из вашей базы данных.

Это вполне приемлемо для создания нового DateTime или ArrayCollection, вы даже можете добавить реляционные объекты по умолчанию. Мне лично нравится вложение ваших зависимостей, поэтому ваш код очень легко тестировать и изменять с минимальными усилиями, но некоторые вещи просто имеют смысл создать экземпляр в вашем конструкторе.

Большинство из этих ответов кажутся разумными. Но я всегда делаю то, что вы делаете. Если я его тестирую, то у меня либо есть сеттер, либо просто что-то задают (видя, что вы «mother_language» общедоступны).

Но я лично думаю, что это зависит от того, что вы делаете. Из кода, который я вижу опубликованным другими, если ваш класс создает объекты, конструктор должен всегда принимать некоторые параметры.

Что делать, если я хочу создать объект, который создает экземпляр другого объекта, где этот внутренний объект создает экземпляр другого объекта и т. Д.? Кажется, у меня будет много на руках.

Для меня это утверждение похоже на то, что он говорит: «Не используйте camelCase, используйте under_score». Я думаю, вы должны делать это, как бы то ни было. В конце концов, у вас есть собственный способ тестирования, не так ли?

Если вы хотите установить значение по умолчанию, но улучшите гибкость / тестируемость, почему бы не сделать это так?

 class Person { protected $mother_language; function __construct(Language $language = null) { $this->mother_language = $language ?: new Language('English'); } } 

Некоторым людям все еще может не понравиться, но это улучшение.