Инициализировать() vs Конструктор(), правильное использование при создании объекта
Мы все знаем разницу между методом Constructor
и определяемым пользователем Initialize()
методом.
Мой вопрос сосредоточен на лучшей практике проектирования для создания объекта. Мы можем поместить весь код Initialize()
в Constructor()
и наоборот (переместите весь код разминки на Initialize
метод и вызовите этот метод из Constructor
).
В настоящее время, создавая новый класс, я создаю любые новые экземпляры внутри Constructor()
и перемещаю любой другой код разминки в метод Initialize()
.
Какой лучший компромисс по вашему мнению?
Ответы
Ответ 1
Я думаю, что нужно учитывать несколько аспектов:
-
Конструктор должен инициализировать объект таким образом, чтобы он находился в рабочем состоянии.
-
Конструктор должен только инициализировать объект, а не выполнять тяжелую работу.
-
Конструктор не должен прямо или косвенно обращаться к виртуальным членам или внешнему коду.
Поэтому в большинстве случаев метод Initialize не требуется.
В тех случаях, когда инициализация включает в себя больше, чем помещение объекта в пригодное для использования состояние (например, когда требуется выполнить тяжелую работу или виртуальные члены или внешние вызовы), тогда метод инициализации является хорошей идеей.
Ответ 2
Я недавно подумал об этом в честной битве (следовательно, нашел этот вопрос), и пока у меня нет ответа, я думал, что поделился своими мыслями.
- Конструкторы "идеально" должны устанавливать только состояние объекта, т.е. несколько:
this.member = member;
По-моему, это хорошо играет с IoC, наследованием, тестированием и просто хорошо пахнет.
Иногда требуется тяжелая работа, поэтому я пытаюсь сделать это:
- Перейдите в тяжелый подъем.
Это означает абстрагирование этого кода инициализации на другой класс и передачу этого. Обычно это возможно, если тяжелый подъем не является на самом деле ответственным за ваши объекты, поэтому это фактически реорганизует более хороший код.
Если это невозможно и вам нужно инициализировать состояние для своего класса перед использованием, добавьте метод initialse. Это добавляет временную зависимость в ваш код, но это не обязательно плохо, особенно при использовании контейнеров IoC:
Скажите CarEngine
требуется DrivingAssistComputer
, а DrivingAssistComputer
необходимо выполнить сильную инициализацию, то есть загрузить все параметры, проверки погодных условий и т.д. Еще одна вещь, которую следует отметить, заключается в том, что CarEngine
не взаимодействует напрямую с DrivingAssistComputer
, ему просто нужно, чтобы он присутствовал, делая свою собственную сторону сбоку. Фактически, двигатель может работать некорректно, если DrivingAssistComputer
не выполняет свою работу в фоновом режиме (меняя какое-то состояние где-то). Если мы используем IoC, мы имеем:
// Without initialise (i.e. initialisation done in computer constructor)
public CarEngine(FuelInjectors injectors, DrivingAssistComputer computer) {
this.injectors = injectors;
// No need to reference computer as we dont really interact with it.
}
...
Итак, здесь мы имеем аргумент конструктора, обозначающий computer
как зависимость, но фактически не использующий его. Так что это уродливо, но давайте добавим метод Initialise:
public CarEngine(FuelInjectors injectors, DrivingAssistComputer computer) {
this.injectors = injectors;
// This ofcourse could also be moved to CarEngine.Initialse
computer.Initialise();
}
...
Все еще не сплоченный класс, но по крайней мере мы знаем, что мы зависим от компьютера, хотя мы не напрямую взаимодействуем с ним вне конструктора.
Другим вариантом является использование CarEngineFactory:
CarEngine CreateEngine(FuelInjectors injectors) {
new DrivingAssistComputer().Initialise();
return new CarEngine(injectors);
}
...
Однако я нахожу заводы и IoC просто путают матрицу, поэтому я бы пошел на второй вариант.
Хотелось бы услышать некоторые мысли об этом.
Изменить 1:
Другой вариант, который я пропустил выше, имеет метод Initialise, но перемещение этого вызова в модуль инициализации IoC. Поэтому создание и инициализация все еще несколько инкапсулированы.