您是否在每个构造函数中检查数据有效性,或者您只是假设数据是正确的并且在参数有问题的特定函数中抛出异常?
答案 0 :(得分:14)
构造函数也是一个函数 - 为什么要区分?
创建对象意味着已完成所有完整性检查。检查构造函数中的参数并在检测到非法值后抛出异常是完全合理的。
其中简化了调试。当程序在构造函数中抛出异常时,您可以观察到堆栈跟踪并经常立即查看原因。如果您延迟检查,则必须进行更多调查以检测先前事件导致当前错误的原因。
答案 1 :(得分:2)
从一开始就拥有一个完全构造的对象,并且所有不变量都“满意”,这总是更好。但请注意,在非托管语言中抛出构造函数中的异常,因为这可能会导致内存泄漏。
答案 2 :(得分:2)
我总是强迫构造函数中的值。如果用户不愿意遵守规则,我只是默默地执行它们而不告诉他们。
所以,如果它们传递的值为107%,我会将其设置为100%。我只是在文档中明确说明发生了什么。
只有在没有明显的逻辑强制的情况下,我才会向他们抛出异常。我喜欢称之为“对那些懒得或愚蠢的人阅读文档时最让人惊讶的原则”。
答案 3 :(得分:1)
如果你引入构造函数,堆栈跟踪更有可能显示错误值的来源。
答案 4 :(得分:0)
我同意一般情况下的sharptooth,但有时候对象可能会有一些状态,其中某些函数是有效的,有些则不是。在这些情况下,最好将检查推迟到具有这些特定依赖关系的函数。
通常你会想要在构造函数中验证,只是因为这意味着你的对象总是处于有效状态。但是某些类型的对象需要特定于功能的检查,这也没关系。
答案 5 :(得分:0)
这是一个棘手的问题。在过去的几年里,我已经多次改变了与这个问题相关的习惯,所以我想到了一些想法:
我认为这实际上取决于具体情况......当我知道参数确实来自外部系统(如用户输入,数据库,Web服务或类似的东西)时,我最终只会编写参数检查。 ...)。如果数据来自我自己的系统,我不会在大多数时间编写测试。
答案 6 :(得分:0)
我总是试图尽早失败。所以我明确地检查了构造函数中的参数。
答案 7 :(得分:0)
理论上,在调用函数之前,调用代码应始终确保满足前提条件。构造函数也是如此。
在实践中,程序员很懒惰,并且确保最好仍然检查前提条件。断言在那里派上用场。
实施例。请原谅我的非大括号语法:
// precondition b<>0
function divide(a,b:double):double;
begin
assert(b<>0); // in case of a programming error.
result := a / b;
end;
// calling code should be:
if foo<>0 then
bar := divide(1,0)
else
// do whatever you need to do when foo equals 0
或者,您可以随时更改前提条件。如果是构造函数,这不是很方便。
// no preconditions.. still need to check the result
function divide(a,b:double; out hasResult:boolean):double;
begin
hasResult := b<>0;
if not hasResult then
Exit;
result := a / b;
end;
// calling code:
bar := divide(1,0,result);
if not result then
// do whatever you need to do when the division failed
答案 8 :(得分:0)
一直尽快失败。运行时展示了这种做法的一个很好的例子。 *如果您尝试为数组使用无效索引,则会出现异常。 *如果不在稍后阶段尝试,则立即失败。
想象一下,如果一个错误的演员表保留在一个引用中,并且每次你试图使用时都会遇到异常,那么灾难代码是什么。唯一明智的做法是尽快失败并尽量避免糟糕/无效的状态。