Когда для конструктора правильно выбрасывать исключение? - PullRequest
195 голосов
/ 17 сентября 2008

Когда для конструктора правильно выбрасывать исключение? (Или в случае с Целью C: когда правомерно вернуть nil?)

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

Ответы [ 25 ]

0 голосов
/ 12 октября 2017

Лучший совет, который я видел в отношении исключений, состоит в том, чтобы генерировать исключение, если и только если альтернативой является невыполнение условия публикации или сохранение инварианта.

Этот совет заменяет неясное субъективное решение (это хорошая идея ) техническим, точным вопросом, основанным на проектных решениях (инвариантных и последующих условиях), которые вы уже должны были принять.

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

Обратите внимание, что добавление set-up в отдельный метод инициализации не позволяет избежать возникновения исключений. Исключения, которые ваш конструктор мог выдать, теперь будут выбрасываться методом инициализации. Все полезные методы вашего класса должны генерировать исключения, если они вызваны для неинициализированного объекта.

Также обратите внимание, что избежать возможности создания исключений вашим конструктором проблематично, и во многих случаях невозможно во многих стандартных библиотеках. Это потому, что дизайнеры этих библиотек считают, что выбрасывать исключения из конструкторов - хорошая идея. В частности, любая операция, которая пытается получить не разделяемый или конечный ресурс (такой как выделение памяти), может завершиться неудачей, и этот сбой обычно указывается в ОО-языках и библиотеках, вызывая исключение.

0 голосов
/ 17 сентября 2008

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

0 голосов
/ 17 сентября 2008

Для меня это несколько философское дизайнерское решение.

Очень хорошо иметь экземпляры, которые действительны до тех пор, пока они существуют, начиная со времени ctor. Для многих нетривиальных случаев это может потребовать выброса исключений из ctor, если невозможно выделить память / ресурсы.

Некоторые другие подходы - это метод init (), который имеет свои собственные проблемы. Одним из них является обеспечение init () на самом деле вызывается.

В одном варианте используется ленивый подход для автоматического вызова init () при первом вызове аксессора / мутатора, но это требует от любого потенциального вызывающего абонента беспокоиться о допустимости объекта. (В отличие от «оно существует, следовательно, это действительная философия»).

Я видел различные предложенные шаблоны проектирования для решения этой проблемы. Например, возможность создания начального объекта через ctor, но необходимость вызова init (), чтобы получить в руки инициализированный объект с аксессорами / мутаторами.

У каждого подхода есть свои взлеты и падения; Я использовал все это успешно. Если вы не создаете готовые к использованию объекты с момента их создания, то я рекомендую большую дозу утверждений или исключений, чтобы пользователи не взаимодействовали до init ().

Добавление

Я писал с точки зрения программистов на C ++. Я также предполагаю, что вы правильно используете идиому RAII для обработки ресурсов, освобождаемых при возникновении исключений.

0 голосов
/ 17 сентября 2008

Я только изучаю Цель C, поэтому я не могу говорить на собственном опыте, но я читал об этом в документах Apple.

http://developer.apple.com/documentation/Cocoa/Conceptual/CocoaFundamentals/CocoaObjects/chapter_3_section_6.html

Он не только скажет вам, как справиться с заданным вами вопросом, но и хорошо объяснит его.

0 голосов
/ 17 сентября 2008

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...