Мне интересно узнать, какую технику вы используете для проверки внутреннего состояния объекта во время операции, которая с его собственной точки зрения может терпеть неудачу из-за плохого внутреннего состояния или инвариантного нарушения.Как вы проверяете внутреннее состояние объекта?
Мой основной акцент делается на C++, так как в C# официальный и распространенным способом является бросить исключение, и в C++ есть не только один одного способ сделать это (хорошо, на самом деле не в C# либо, я знаю, что).
Отметьте, что я не, говоря о проверке параметров функции, но скорее как проверки целостности класса.
Например, предположим, что мы хотим, чтобы объект Printer
выполнял асинхронное задание печати Queue
. Пользователю Printer
эта операция может быть успешной только потому, что результат асинхронной очереди приходит в другое время. Таким образом, для вызывающего абонента нет соответствующего кода ошибки.
Но к объекту Printer
эта операция может завершиться неудачно, если внутреннее состояние плохое, то есть инвариант класса сломан, что в основном означает: ошибка. Это условие не обязательно представляет интерес для пользователя объекта Printer
.
Лично я склонен смешивать три стиля проверки внутреннего состояния, и я не могу решить, какой из них лучший, если таковой имеется, только тот, который является абсолютно худшим. Я хотел бы услышать ваши взгляды на них, а также поделиться своим опытом и мыслями по этому вопросу.
Первый стиль я использую - лучше потерпеть неудачу в контролируемом образе, чем коррумпированные данные:
void Printer::Queue(const PrintJob& job)
{
// Validate the state in both release and debug builds.
// Never proceed with the queuing in a bad state.
if(!IsValidState())
{
throw InvalidOperationException();
}
// Continue with queuing, parameter checking, etc.
// Internal state is guaranteed to be good.
}
Второго стиль я использую - лучше аварии неуправляемый, чем коррумпированные данные:
void Printer::Queue(const PrintJob& job)
{
// Validate the state in debug builds only.
// Break into the debugger in debug builds.
// Always proceed with the queuing, also in a bad state.
DebugAssert(IsValidState());
// Continue with queuing, parameter checking, etc.
// Generally, behavior is now undefined, because of bad internal state.
// But, specifically, this often means an access violation when
// a NULL pointer is dereferenced, or something similar, and that crash will
// generate a dump file that can be used to find the error cause during
// testing before shipping the product.
}
Третий стиль Я использую - лучше молча и защищенно, чем поврежденные данные:
void Printer::Queue(const PrintJob& job)
{
// Validate the state in both release and debug builds.
// Break into the debugger in debug builds.
// Never proceed with the queuing in a bad state.
// This object will likely never again succeed in queuing anything.
if(!IsValidState())
{
DebugBreak();
return;
}
// Continue with defenestration.
// Internal state is guaranteed to be good.
}
Мой commen ts к стилям:
- Я думаю, что предпочитаю второй стиль, где отказ не скрыт, при условии, что нарушение прав действительно вызывает сбой.
- Если это не указатель NULL, связанный с инвариантом, то я склоняюсь к первому стилю.
- Мне действительно не нравится третий стиль, так как он скроет множество ошибок, но я знаю людей, которые предпочитают его в производственном коде, потому что он создает иллюзию надежного программного обеспечения, которое не падает (функции просто перестанут функционировать , как в очереди на сломанном объекте
Printer
).
Вы предпочитаете какой-либо из них или у вас есть другие способы достижения этого?
Я вообще не согласен с тем, что NVI - это хорошее решение в конкретном случае, о котором я говорил. Было бы хорошо, если бы Принтер был базовым классом, но добавив, что проводка до необходимость очевидна, часто бывает напрасно. Если я увижу необходимость вывести из принтера, тогда я бы реорганизовал в то время. – 2008-12-18 10:28:57