Я веду дискуссию с моим коллегой о том, сколько работы может сделать конструктор. У меня есть класс B, который внутренне требует другого объекта A. Объект A - один из немногих членов, которым класс B должен выполнять свою работу. Все его открытые методы зависят от внутреннего объекта A. Информация об объекте A хранится в БД, поэтому я пытаюсь проверить и получить ее, просмотрев ее в БД в конструкторе. Мой коллега отметил, что конструктор не должен выполнять большую работу, кроме как записывать параметры конструктора. Так как все публичные методы в любом случае потерпят неудачу, если объект A не будет найден с использованием входных данных для конструктора, я утверждал, что вместо того, чтобы разрешить создание экземпляра и последующий сбой, на самом деле лучше бросить конструктор раньше.
Что думают другие? Я использую C #, если это имеет какое-либо значение.
Чтение Есть ли когда-нибудь причина выполнять всю работу с объектом в конструкторе? мне интересно, что выбор объекта A путем перехода в DB является частью «любой другой инициализации, необходимой для того, чтобы сделать объект готовым к использованию», потому что, если пользователь передал неверные значения конструктору, я не смог бы использовать ни один из его открытых методов.
Конструкторы должны создавать экземпляры полей объекта и выполнять любую другую инициализацию, необходимую для готовности объекта к использованию. Обычно это означает, что конструкторы небольшие, но есть сценарии, в которых это будет значительный объем работы.
источник
Ответы:
Ваш вопрос состоит из двух совершенно разных частей:
Должен ли я выбросить исключение из конструктора, или у меня должен быть метод сбой?
Это явно применение принципа Fail-fast . Сделать сбой конструктора гораздо легче, чем найти причину сбоя метода. Например, вы можете получить экземпляр, уже созданный из какой-то другой части кода, и вы получите ошибки при вызове методов. Очевидно ли, что объект создан неправильно? Нет .
Что касается проблемы «обернуть вызов в try / catch». Исключения должны быть исключительными . Если вы знаете, что какой-то код вызовет исключение, вы не заключаете код в try / catch, но проверяете параметры этого кода перед выполнением кода, который может вызвать исключение. Исключения предназначены только для того, чтобы система не попала в недопустимое состояние. Если вы знаете, что некоторые входные параметры могут привести к неверному состоянию, убедитесь, что эти параметры никогда не встречаются. Таким образом, вы должны делать try / catch только в тех местах, где вы можете логически обработать исключение, которое обычно находится на границе системы.
Могу ли я получить доступ к «другим частям системы, например БД» из конструктора.
Я думаю, что это идет вразрез с принципом наименьшего удивления . Не многие ожидают, что конструктор получит доступ к БД. Так что нет, вы не должны этого делать.
источник
Тьфу.
Конструктор должен делать как можно меньше - проблема в том, что поведение исключений в конструкторах неудобно. Очень немногие программисты знают, что такое правильное поведение (включая сценарии наследования), и принуждение ваших пользователей пробовать / ловить каждую инстанциацию ... в лучшем случае болезненно.
В этом есть две части: в конструкторе и с помощью конструктора. Это неудобно в конструкторе, потому что вы не можете сделать там много, когда возникает исключение. Вы не можете вернуть другой тип. Вы не можете вернуть ноль. Вы можете перебросить исключение, вернуть сломанный объект (плохой конструктор) или (в лучшем случае) заменить некоторую внутреннюю часть нормальным значением по умолчанию. Использовать конструктор неудобно, потому что тогда ваши экземпляры (и экземпляры всех производных типов!) Могут быть сгенерированы. Тогда вы не можете использовать их в инициализаторах членов. Вы в конечном итоге используете исключения для логики, чтобы увидеть «удалось ли моему созданию?», За исключением читабельности (и хрупкости), вызванной попыткой / отловом везде.
Если ваш конструктор делает нетривиальные вещи или вещи, которые, как можно разумно ожидать, потерпят неудачу, рассмотрите статический
Create
метод (с непубличным конструктором). Он намного удобнее в использовании, его легче отлаживать, и, в случае успеха, он все еще дает вам полностью сконструированный экземпляр.источник
Connection
объект можетCreateCommand
для вас, чтобы вы знали, что команда правильно подключена.Конструктор - баланс сложности метода - действительно вопрос обсуждения хорошего стиля. Довольно часто используется пустой конструктор
Class() {}
с методомInit(..) {..}
для более сложных вещей. Среди аргументов, которые необходимо принять во внимание:Class x = new Class(); x.Init(..);
много раз, частично в модульных тестах. Не так хорошо, однако, кристально чистый для чтения. Иногда я просто помещаю их в одну строку кода.источник
init
метода в том, что обработка ошибок намного проще. В частности, возвращаемое значениеinit
метода может указывать, успешна ли инициализация, и метод может гарантировать, что объект всегда находится в хорошем состоянии.