Summary: 某一段代码须要对程序状态作出某种假设。以断言明确这表现这种假设。java
动机: 经常会有这样一段代码:只有当某个条件为真时,该段代码才能正常运行。例如平方根计算只对正直才能进行,又例如某个对象可能假设其字段至少有一个不等于null。程序员
这样的假设一般并无在代码中明确表现出来,你必须阅读整个算法才能看出。有时程序员会以注释写出这样的假设。而咱们要介绍的是一种更好的技术:使用断言明确标明这些假设。算法
断言首先是一个条件表达式,应该老是为真。若是它失败,表示程序员犯了错误。所以断言的失败应该致使一个非受控异常(unchecked exception)。断言绝对不能被系统的其余部分使用。实际上,程序最后的成品每每将断言通通删除。所以标记“某些东西是个断言”是很重要的。函数
断言能够做为交流与调试的辅助。在交流的角度上,断言能够帮助程序阅读者理解代码所作的假设;在调试的角度上,断言能够在距离bug最近的地方抓住它们。spa
作法:指针
若是程序员不犯错,断言就应该不会对系统运行形成任何影响,因此加入断言永远不会影响程序的行为。调试
若是你发现代码假设某个条件始终为真,就加入一个断言明确说明这种状况。code
à你能够新建一个Assert类,用于处理各类状况下的断言。对象
注意,不要滥用断言。请不要使用它来检查“你认为应该为真”的条件,请只用它来检查“必定必须为真”的条件。滥用断言可能会形成难以维护的重复逻辑。在一段逻辑中加入断言是有好处的,由于它迫使你从新考虑这段代码的约束条件。若是不知足这些约束条件,程序也能够正常运行,断言就不会带给你任何帮助,只会把代码变得混乱,而且有可能妨碍之后的修改。get
你应该经常问本身:若是断言指示的约束条件不能知足,代码是否仍能正常运行?若是能够,就把断言拿掉。
另外,还须要注意断言中的重复代码。它们合其余任何地方的重复代码同样很差闻。你能够大胆使用Extract Method去掉那些重复代码。
范例:
下面是一个简单的例子:开支限制。后勤部门的员工每月有固定的开支限额;业务部门的员工则按照项目的开支限额来控制本身的开支。一个员工可能没有开支额度可用,也可能没有参与项目,但二者总要有一个(不然就没有经费可用了)。在开支限额相关程序中,上述假设老是成立的,所以:
class Employee... private static final double NULL_EXPENSE = -1.0 private doulbe _expenseLimit = NULL_EXPENSE; private Project _primaryProject; double getExpenseLimt(){ return (_expenseLimit != NULL_EXPENSE)? _expenseLimit : _primaryProject.getMemberExpenseLimit(); } boolean withinLimint(doulbe expenseAmount){ return (expenseAmount <= getExpenseLimit()); }
这段代码包含了一个明显假设:任何员工要么参与某个项目,要么有我的开支限额。咱们可使用断言在代码中更明确地指出这一点:
double getExpenseLimt(){ Assert.isTrue(_expenseLimit != NULL_EXPENSE || _primaryProject !=null); return (_expenseLimit != NULL_EXPENSE)? _expenseLimit : _primaryProject.getMemberExpenseLimit(); }
这条断言不会改变程序的任何行为。另外一方面,若是断言中的条件不为真,咱们就会受到一个运行期异常:也许是withinLimit()函数抛出一个空指针异常,也许是在Assert.isTrue()函数中抛出一个运行期异常。有时断言能够帮助程序员找到bug,由于它离出错地点很近。可是,更多时候,断言的价值在于:帮助程序员理解代码正确运行的必要条件。