使用assert(Java等)的一些(反)模式 [英] Some (anti-)patterns on using assert (Java, and others)

查看:140
本文介绍了使用assert(Java等)的一些(反)模式的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

最后,我有一个问题要问Stack Overflow! : - )

Finally, I have a question to ask on Stack Overflow! :-)

主要目标是Java,但我认为它主要与语言无关:如果你没有本机断言,你可以随时模拟它。

The main target is for Java but I believe it is mostly language agnostic: if you don't have native assert, you can always simulate it.

我在一家销售一套用Java编写的软件的公司工作。代码很旧,至少可以追溯到Java 1.3,在某些地方,它显示......这是一个很大的代码库,大约有两百万行,所以我们不能一次重构它。

最近,我们将最新版本从Java 1.4语法和JVM切换到Java 1.6,保守地使用了一些新功能,如 assert (我们曾经使用过DEBUG。 ASSERT宏 - 我知道断言已在1.4中引入但我们之前没有使用过),泛型(只有类型集合),foreach循环,枚举等。

I work for a company selling a suite of softwares written in Java. The code is old, dating back to Java 1.3 at least, and at some places, it shows... That's a large code base, some 2 millions of lines, so we can't refactor it all at once.
Recently, we switched the latest versions from Java 1.4 syntax and JVM to Java 1.6, making conservative use of some new features like assert (we used to use a DEBUG.ASSERT macro -- I know assert has been introduced in 1.4 but we didn't used it before), generics (only typed collections), foreach loop, enums, etc.

虽然我已经阅读了几篇关于这个主题的文章,但我仍然对使用assert感到有些偏见。然而,我看到的一些用法让我感到困惑,伤害了我的常识... ^ _ ^所以我想我应该问一些问题,看看我是否正确想要纠正的东西,或者它是否违背了常规做法。我很啰嗦,所以我加粗这些问题,对于那些喜欢浏览东西的人来说。

I am still a bit green about the use of assert, although I have read a couple of articles on the topic. Yet, some usages I see leave me perplex, hurting my common sense... ^_^ So I thought I should ask some questions, to see if I am right to want to correct stuff, or if it goes against common practices. I am wordy, so I bolded the questions, for those liking to skim stuff.

作为参考,我搜索了断言java 在SO中发现了一些有趣的线程,但显然没有完全重复。

For reference, I have searched assert java in SO and found some interesting threads, but apparently no exact duplicate.

  • How to avoid "!= null" statements in java? and How much null checking is enough? are quite relevant, because lot of asserts we have just check if variable is null. At some places in our code, there are usages of the null object (eg. returning new String[0]) but not always. We have to live with that, at least for maintenance of legacy code.
  • Some good answers also in Java assertions underused.
  • Oh, and SO indicates with reason that When should I use Debug.Assert()? question is related too (nice feature to reduce duplicates!).

首先,主要问题,今天引发了我的问题:

First, main issue, which triggered my question today:

SubDocument aSubDoc = documents.GetAt( i );
assert( aSubDoc != null );
if ( aSubDoc.GetType() == GIS_DOC )
{
   continue;
}
assert( aSubDoc.GetDoc() != null );
ContentsInfo ci = (ContentsInfo) aSubDoc.GetDoc();

是的,我们使用MS的C / C ++样式/代码约定。我甚至喜欢它(来自相同的背景)!所以起诉我们。

首先, assert()表来自转换 DEBUG.ASSERT()调用。我不喜欢额外的括号,因为assert是一个语言结构,而不是(不再是,这里)一个函数调用。我也不喜欢 return(foo); :-)

接下来,断言不在这里测试不变量,它们更像是用来防范坏的价值观。但正如我所理解的那样,它们在这里毫无用处:assert将抛出一个异常,甚至没有记录伴随字符串,并且只有在启用断言时才会抛出异常。因此,如果我们有 -ea 选项,我们只会抛出一个断言,而不是常规的NullPointerException。这看起来不是最重要的优势,因为我们无论如何都会在最高级别捕获未经检查的异常。

我是否正确地假设我们可以摆脱它们并与之共存(即让Java提升)这样的未解决的例外)?(或者,当然,如果可能的话,测试空值,这在其他地方进行)。

(Yes, we use MS' C/C++ style/code conventions. And I even like it (coming from same background)! So sue us.)
First, the assert() form comes from conversion of DEBUG.ASSERT() calls. I dislike the extra parentheses, since assert is a language construct, not (no longer, here) a function call. I dislike also return (foo); :-)
Next, the asserts don't test here for invariants, they are rather used as guards against bad values. But as I understand it, they are useless here: the assert will throw an exception, not even documented with a companion string, and only if assertions are enabled. So if we have -ea option, we just have an assertion thrown instead of the regular NullPointerException one. That doesn't look like a paramount advantage, since we catch unchecked exceptions at highest level anyway.
Am I right supposing we can get rid of them and live with that (ie. let Java raise such unckecked exception)? (or, of course, test against null value if likely, which is done in other places).

旁注:我应该必须在上面的片段中断言,我会对ci值进行断言,而不是针对getter:即使大多数getter都经过优化/内联,我们也无法确定,所以我们应该避免两次调用它。

Side note: should I have to assert in the above snippet, I would do that against ci value, not against the getter: even if most getters are optimized/inlined, we cannot be sure, so we should avoid calling it twice.

有人告诉我,在最后一个引用的线程中,公共方法应该使用针对参数值的测试(公共API的使用),私有方法应该依赖于断言。好建议。

现在,两种方法都必须检查另一个数据源:外部输入。 IE浏览器。例如,来自用户,来自数据库,来自某个文件或来自网络的数据。

在我们的代码中,我看到针对这些值的断言。我总是将这些更改为真正的测试,因此即使禁用断言它们也会起作用:这些不是不变量并且必须正确处理。

我只看到一个可能的异常,其中输入应该是常量,例如数据库表填充了关系中使用的常量:如果此表已更改但相应的代码未更新,程序将中断。

您是否看到其他异常?

Somebody told, in the last referenced thread, that public methods should use tests against values of parameters (usage of the public API) and private methods should rely on asserts instead. Good advice.
Now, both kinds of methods must check another source of data: external input. Ie. data coming from user, from a database, from some file or from the network, for example.
In our code, I see asserts against these values. I always change these to real test, so they act even with assertions disabled: these are not invariants and must be properly handled.
I see only one possible exception, where input is supposed constant, for example a database table filled with constants used in relations: program would break if this table is changed but corresponding code wasn't updated.
Do you see other exceptions?

我看到的另一个相对频繁的用途,似乎没问题:在交换机的默认情况下,或在一系列的结尾处,如果测试所有可能的值(这些情况可以追溯到我们使用枚举之前!),通常有一个断言错误:东西的意外值:+东西;

对我来说看起来合法(这些情况不应该在生产中发生),您怎么看?(超出无开关,使用OO这些与此无关的建议)。

Another relatively frequent use I see, which seems OK: in the default of a switch, or at the end of a series of else if testing all possible values (these cases date back before our use of enums!), there is often an assert false : "Unexpected value for stuff: " + stuff;
Looks legitimate for me (these cases shouldn't happen in production), what do you think? (beyond the "no switch, use OO" advices which are irrelevant here).

最后,还有其他有用的用例或讨厌的陷阱我错过了吗? (可能!)

And finally, are there any other useful use cases or annoying gotchas I missed here? (probably!)

推荐答案

头号规则是避免断言中的副作用。换句话说,代码应该与关闭断言的行为完全相同,因为断言打开时没有失败(显然失败的断言会改变行为,因为它们会引发错误)。

The number one rule is to avoid side-effects in assertions. In other words, the code should behave identically with assertions turned off as it does when assertions are turned on and not failing (obviously assertions that fail are going to alter the behaviour because they will raise an error).

第二条规则是不使用断言进行基本检查。它们可以关闭(或者更准确地说,不能打开)。对于非私有方法的参数检查,请使用IllegalArgumentException。

The number two rule is not to use assertions for essential checks. They can be turned off (or, more correctly, not turned on). For parameter-checking of non-private methods use IllegalArgumentException.

断言是可执行的假设。我使用断言表明我对该程序当前状态的看法。例如,我假设n在这里是正面的,或者我假设列表中恰好有一个元素

Assertions are executable assumptions. I use assertions to state my beliefs about the current state of the program. For example, things like "I assume that n is positive here", or "I assume that the list has precisely one element here".

这篇关于使用assert(Java等)的一些(反)模式的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆