Я все еще считаю себя учеником-программистом, поэтому я всегда стремлюсь найти «лучший» способ для типичного программирования. Сегодня мой коллега утверждал, что мой стиль кодирования выполняет какую-то ненужную работу, и я хочу услышать мнения других. Как правило, когда я проектирую класс на языке ООП (обычно C ++ или Python), я делю инициализацию на две разные части:
class MyClass1 {
public:
Myclass1(type1 arg1, type2 arg2, type3 arg3);
initMyClass1();
private:
type1 param1;
type2 param2;
type3 param3;
type4 anotherParam1;
};
// Only the direct assignments from the input arguments are done in the constructor
MyClass1::myClass1(type1 arg1, type2 arg2, type3 arg3)
: param1(arg1)
, param2(arg2)
, param3(arg3)
{}
// Any other procedure is done in a separate initialization function
MyClass1::initMyClass1() {
// Validate input arguments before calculations
if (checkInputs()) {
// Do some calculations here to figure out the value of anotherParam1
anotherParam1 = someCalculation();
} else {
printf("Something went wrong!\n");
ASSERT(FALSE)
}
}
(или, эквивалент Python)
class MyClass1:
def __init__(self, arg1, arg2, arg3):
self.arg1 = arg1
self.arg2 = arg2
self.arg3 = arg3
#optional
self.anotherParam1 = None
def initMyClass1():
if checkInputs():
anotherParam1 = someCalculation()
else:
raise "Something went wrong!"
Что вы думаете об этом подходе? Должен ли я воздерживаться от разделения процесса инициализации? Этот вопрос не ограничивается только C ++ и Python, и ответы на другие языки также приветствуются.
design
object-oriented
coding-style
Caladbolgll
источник
источник
Ответы:
Хотя иногда это проблематично, есть много преимуществ для инициализации всего в конструкторе:
initMyClass1()
потому что его там нет . «Самые дешевые, быстрые и надежные компоненты - это те, которых нет».источник
Подумайте об абстракции, которую вы предоставляете своим пользователям.
Зачем делить то, что можно сделать одним выстрелом, на два?
Дополнительная инициализация - это просто нечто дополнительное для программистов, использующих ваш API, для запоминания, и предоставляет больше возможностей для неправильной работы, если они не делают это правильно, но для чего им это стоит за эту дополнительную нагрузку?
Вы хотите предоставить мертвую простую, удобную в использовании, сложную ошибочную абстракцию. Программирование достаточно сложно без каких-либо ненужных вещей, которые нужно помнить / обручи, чтобы прыгать. Вы хотите, чтобы ваши пользователи API (даже если вы используете собственный API) попали в пропасть успеха .
источник
Инициализируйте все, кроме области больших данных. Инструменты статического анализа помечают поля, не инициализированные в конструкторе. Однако наиболее продуктивный / безопасный способ - иметь все переменные-члены с конструкторами по умолчанию и явно инициализировать только те, которые требуют инициализации не по умолчанию.
источник
Есть случаи, когда объект имеет большую инициализацию, которую можно разделить на две категории:
Атрибуты, которые являются неизменяемыми или не требуют сброса.
Атрибуты, для которых может потребоваться возврат к исходным значениям (или шаблонным значениям) на основе некоторого условия после выполнения их работы, что-то вроде мягкого сброса. например, соединения в пуле соединений.
Здесь вторая часть инициализации, хранящаяся в отдельной функции, скажем InitialiseObject (), может быть вызвана в ctor.
Эта же функция может быть вызвана позже, если требуется программный сброс без необходимости сбрасывать и воссоздавать объект.
источник
Как уже говорили другие, обычно хорошей идеей является инициализация в конструкторе.
Есть, однако, причины, по которым это не может или не может применяться в конкретных случаях.
Обработка ошибок
Во многих языках единственный способ сообщить об ошибке в конструкторе - вызвать исключение.
Если ваша инициализация имеет разумную вероятность возникновения ошибки, например, она связана с вводом-выводом или ее параметры могут быть введены пользователем, тогда единственный доступный вам механизм - вызвать исключение. В некоторых случаях это может быть не тем, что вам нужно, и может иметь смысл выделить код, подверженный ошибкам, в отдельную функцию инициализации.
Вероятно, наиболее распространенным примером этого является C ++, если стандарт проекта / организации должен отключать исключения.
Государственный аппарат
Это тот случай, когда вы моделируете объект, который имеет явные переходы состояний. Например, файл или сокет, который можно открывать и закрывать.
В этом случае, как правило, конструкция (и удаление) объекта имеют дело только с ориентированными на память атрибутами (именем файла, портом и т. Д.). Затем появятся функции для конкретного управления переходами состояний, например, открытие, закрытие, которые фактически являются функциями инициализации и удаления.
Преимущества заключаются в обработке ошибок, как и выше, но также может быть случай отделения конструкции от инициализации (скажем, вы строите вектор файлов и открываете их асинхронно).
Недостаток, как уже говорили другие, заключается в том, что вы теперь возлагаете бремя управления состоянием на пользователей ваших классов. Если бы вы могли управлять только конструкцией, то вы могли бы, скажем, использовать RAII, чтобы сделать это автоматически.
источник