Я рассказал коллеге, почему конструктор, вызывающий метод, может быть антипаттерном.
пример (в моем ржавом C ++)
class C {
public :
C(int foo);
void setFoo(int foo);
private:
int foo;
}
C::C(int foo) {
setFoo(foo);
}
void C::setFoo(int foo) {
this->foo = foo
}
Я хотел бы лучше мотивировать этот факт через ваш дополнительный вклад. Если у вас есть примеры, ссылки на книги, страницы блогов или названия принципов, они будут очень рады.
Редактировать: я говорю в целом, но мы кодируем на Python.
this
любой из методов, которые вы вызываете из конструктора.Ответы:
Вы не указали язык.
В C ++ конструктор должен быть осторожен при вызове виртуальной функции, так как фактическая функция, которую он вызывает, является реализацией класса. Если это чисто виртуальный метод без реализации, это будет нарушением прав доступа.
Конструктор может вызывать не виртуальные функции.
Если ваш язык - Java, где функции по умолчанию являются виртуальными, имеет смысл быть особенно осторожным.
C #, кажется, справляется с ситуацией так, как вы ожидаете: вы можете вызывать виртуальные методы в конструкторах, и это вызывает самую финальную версию. Так что в C # не анти-паттерн.
Распространенная причина вызова методов из конструкторов заключается в том, что у вас есть несколько конструкторов, которые хотят вызвать общий метод init.
Обратите внимание, что деструкторы будут иметь ту же проблему с виртуальными методами, поэтому вы не можете иметь виртуальный метод «очистки», который находится вне вашего деструктора и ожидать, что он будет вызван деструктором базового класса.
У Java и C # нет деструкторов, у них есть финализаторы. Я не знаю поведение с Java.
C #, кажется, справляется с очисткой правильно в этом отношении.
(Обратите внимание, что, хотя Java и C # имеют сборку мусора, она управляет только распределением памяти. Есть другая очистка, которую должен сделать ваш деструктор, которая не освобождает память).
источник
Хорошо, теперь , что путаница в отношении методов класса против методов экземпляра прояснилась, я могу дать ответ :-)
Проблема не в вызове методов экземпляра вообще из конструктора; это с вызовом виртуальных методов (прямо или косвенно). И главная причина в том, что, находясь внутри конструктора, объект еще не полностью построен . И особенно его части подкласса не создаются вообще, пока выполняется конструктор базового класса. Таким образом, его внутреннее состояние несовместимо в зависимости от языка, и это может привести к различным тонким ошибкам на разных языках.
C ++ и C # уже обсуждались другими. В Java будет вызываться виртуальный метод самого производного типа, однако этот тип еще не инициализирован. Таким образом, если этот метод использует какие-либо поля из производного типа, эти поля могут еще не инициализироваться должным образом в данный момент времени. Эта проблема подробно обсуждается в Effecive Java 2nd Edition , Item 17: Разработка и документирование для наследования, или же запретить ее .
Обратите внимание, что это частный случай общей проблемы преждевременной публикации ссылок на объекты . Методы экземпляра имеют неявный
this
параметр, но передачаthis
явно в метод может вызвать аналогичные проблемы. Особенно в параллельных программах, где, если ссылка на объект преждевременно опубликована в другом потоке, этот поток уже может вызывать методы для него до завершения работы конструктора в первом потоке.источник
Я бы не стал считать вызовы методов самим антипаттерном, скорее запахом кода. Если класс предоставляет
reset
метод, который возвращает объект в его первоначальное состояние, то вызовreset()
в конструкторе - DRY. (Я не делаю никаких заявлений о методах сброса).Вот статья, которая может помочь удовлетворить вашу апелляцию: http://misko.hevery.com/code-reviewers-guide/flaw-constructor-does-real-work/
Дело не в вызове методов, а в конструкторах, которые делают слишком много. ИМХО, вызов методов в конструкторе - это запах, который может указывать на то, что конструктор слишком тяжелый.
Это связано с тем, насколько просто протестировать ваш код. Причины включают в себя:
Модульное тестирование включает в себя много творений и разрушений, поэтому строительство должно быть быстрым.
В зависимости от того, что делают эти методы, это может затруднить тестирование отдельных блоков кода, не полагаясь на некоторые (потенциально не проверяемые) предварительные условия, установленные в конструкторе (например, получение информации из сети).
источник
С точки зрения философии, цель конструктора - превратить необработанный кусок памяти в экземпляр. Пока конструктор выполняется, объект еще не существует, поэтому вызов его методов - плохая идея. В конце концов, вы, возможно, не знаете, что они делают внутри, и они могут по праву считать, что объект, по крайней мере, существует (дух!), Когда их вызывают.
Технически, в этом нет ничего плохого, в C ++, особенно в Python, вам нужно быть осторожным.
Практически, вы должны ограничивать вызовы только такими методами, которые инициализируют членов класса.
источник
Это не вопрос общего назначения. Это проблема в C ++, особенно при использовании наследования и виртуальных методов, потому что создание объектов происходит в обратном направлении, и указатели (таблицы) vtable сбрасываются с каждым уровнем конструктора в иерархии наследования, поэтому, если вы вызываете виртуальный метод, вы можете этого не делать в итоге получится тот, который на самом деле соответствует классу, который вы пытаетесь создать, что лишает смысла использование виртуальных методов.
В языках с нормальной поддержкой ООП, которые правильно устанавливают указатель vtable с самого начала, этой проблемы не существует.
источник
Есть две проблемы с вызовом метода:
В вызове вспомогательной функции нет ничего плохого, если только она не выпала в двух предыдущих случаях.
источник
Я не покупаю это. В объектно-ориентированной системе вызов метода - это почти единственное, что вы можете сделать. Фактически, это более или менее определение «объектно-ориентированного». Итак, если конструктор не может вызывать какие-либо методы, то что он может делать?
источник
В теории ООП это не должно иметь значения, но на практике каждый язык программирования ООП обрабатывает конструкторы по-разному . Я не часто использую статические методы.
В C ++ и Delphi. Если бы мне пришлось задавать начальные значения для некоторых свойств («членов поля»), а код очень расширен, я добавляю несколько вторичных методов в качестве расширения конструкторов.
И не вызывайте другие методы, которые делают более сложные вещи.
Что касается свойств "методы получения и получения", я обычно использую закрытые / защищенные переменные для хранения их состояния, а также методы "получения и установки".
В конструкторе я присваиваю значения «по умолчанию» полям состояния свойств, БЕЗ вызова «аксессоров».
источник