一、ADT的设计
静态检查、动态检查、使用不可变的类型、值、引用等都有助于减少bug。
bug是不可能完全避免的,要将bug限定在一个小范围内,使得程序尽早出问题。例如下图,前置条件要求x>=0,虽然说违反了前置条件方法怎么做都行,但最好还是今早报错。
二、断言
开发阶段在代码中嵌入,检验某些条件是否成立。如果断言失败,会抛出AssertionError。
断言有两种形式,第二种中的message是在断言失败的时候抛出的信息:
在控制流复杂时可以使用断言机制,确保程序永远不会执行到某个地方。但如果程序发布出去运行了,最好不要使用assert而是直接抛出AssertionError,因为assert有可能被取消:
断言主要在开发阶段使用,用来避免引入错误和发现bug,在实际运行阶段,一般不再使用断言,避免降低性能。
不要在断言中写一些程序需要执行的语句,因为断言是可能被取消的,比如说某一步是从列表中删除元素,要确保列表中有这个元素并且成功删除了:
对于程序外部的事情,不要使用断言,例如文件不存在等。断言只用来判断代码是否有问题、是否符合规约,外部的问题需要用异常机制去处理(6-2)。
断言用来确保正确性,异常用来确保健壮性;断言来处理绝不应该发生的情况、错误的情况,异常来处理你预料到可能发生的不正常情况。
对于前置和后置条件:
前置条件:如果参数来自于外部(public),则使用异常处理;如果参数来自内部(private),使用断言去处理,因为这时如果assert出错,说明别的方法的代码有错误。
后置条件:无论public还是private的方法,都可以使用断言来判断后置条件,因为后置条件是程序运行得到的,这时断言出错就说明是程序中的错误。
三、防御式编程
1、处理非法输入
检查,抛出异常或者退出。
2、隔离舱
接收到的外部数据都认为是dirty的,要处理干净。例如将输入的各种是、否、yes、no、Yes转换成true、false等等。
隔离舱外部的函数应该使用异常处理,这一部分是不能确保正确的;隔离舱内部应该用断言,因为隔离舱要确保这些数据的正确性。
此外,防御式编程还会增加代码复杂性,提高运行成本,而且防御式代码本身也可能存在着缺陷。总而言之,我们需要针对实际问题,制定好相应的防御策略。