三. 断言与防御式编程
1.断言
断言主要是开发阶段使用,用于假设某些假设是否成立,一旦不成立则会抛出AssertionError
有以下两种形式:
(1).assert 表达式; 相当于一个if判断,只是失败了就抛出异常。
(2).assert 表达式:信息; 额外在抛出异常的时候显示出对应的信息。
断言一般用在各种不变量,以及前置条件和后置条件的检查等等;但不要胡乱使用断言,断言中也不要有与正常操作有关的代码。
断言很影响运行效率,在开发结束后应该关闭断言来使用。只有在运行时加上-ea参数才会开启断言。
assert的使用范围很广,主要使用在内部不变量、RI、控制流不变量(swtich-case的default)、方法的前置条件、后置条件
断言主要用于开发阶段,避免引入和帮助发现bug。实际运行阶段,不再使用断言,避免降低性能
assert的坏处,大概是assert也会带来一些性能开销而且会让代码混乱。不要将功能性代码写condition里面,例如assert list.remove(x)。一旦assert被disable了。
对于外部问题也不要乱用assert,它只是检查程序的内部状态是否符合规约,断言一旦false程序就停止执行,而外部错误是无法控制的,所以还是应该用异常机制去处理
2.断言vs异常
断言用于保证程序的正确性,如果断言失败那就是严重的错误,一般针对于自己写的代码;而异常用于保证程序的健壮性,有异常不代表一定是严重的错误,一般针对于外部的情况,比如用户输入不合法,读入文件失败等等与自己代码无关的错误。
3.防御式编程
有一种观点认为不应该用assert检测前置和后置条件,因为无论如何都应该被遵守和保证,即使被违反也应该抛出具体的RuntimeException
如果参数来自外部(不受自己控制)就是用异常处理;如果来自于自己所写的其他代码,可以使用断言来帮助发现错误(例如后置条件)
按照惯例,public方法上的前置条件是通过抛出特定的、指定的异常的显式检查来进行的
可以使用断言来测试非公共方法的前置条件,当认为无论client 对该类做什么,这些前置条件都正确时(正确性不由client决定)。但通常这样的方法也不写规约。
在public和nonpublic 方法中,都可以用断言来处理后置条件
断言和异常处理都可以处理同样的错误,开发阶段用断言尽可能消除bugs,在发行版本里用异常处理机制处理漏掉的错误
关于防御式编程,“垃圾进,垃圾出”就不太好,因为输出的垃圾可能会带来更多的问题。所以比较好的做法是什么都不输出、报错或者根本不允许垃圾进来
对来自外部的数据源要仔细检查,例如:文件、网络数据、用户输入等
对每个函数的输入参数合法性要做仔细检查,并决定如何处理非法输入
一种做法是设置路障。隔栏是一种容损策略,起到防火墙的作用
类的public 方法接收到的外部数据都应被认为是不安全的,需要处理干净再传递private 方法——这个就是隔离舱的作用。操作间技术应该也差不多,就是说东西在进入操作间之前要进行一系列处理“隔离舱”外部的函数应使用异常处理,“隔离舱”内的函数应使用断言。Proxy设计模式就是一种隔离方式。
防御式编程本身也有一些缺陷:增加了复杂性、运行时间和维护成本;防御性代码本身也有可能存在缺陷。因此需要设计好何处进行防御,制定相应的防御策略
四.代码调试
当各种防御式编程手段和测试手段还不能完全找出问题后,出现bug就要进行最后一步了:代码调试(debug)。这是一个很浪费时间的步骤,尤其是耦合度高的代码。
1.调试过程
总的来说可以分为四个阶段:重现、诊断、修复、反思。常用方法:假设-检验法。
重现很多时候是非常难的,因为可能有各种因素:多线程,与时间有关,与版本有关,bug导致崩溃而不知为何等等。
高效诊断的方式:测量(加入插桩代码),分治(逐渐缩小错误出现的可能范围),切片(如果某变量的计算有bug,那就只去找对应的与这个变量有关的计算的语句),寻找差异(利用版本控制系统,还有软硬件差异等等)
2.调试工具
(1).内存转储:把某一时刻软硬件的信息、出问题的部分的内存的情况以及进程线程的有关信息保存在文件中。是最原始,难以理解的方式。
(2).栈轨迹:可以很轻松的定位到问题所在的方法的某一行
(3).日志:比较流行的是log4j,可以灵活的使用日志。
(4).警告信息:尽量把编译器的警告级别调到最高级,尽量消除一切Warning.
3.调试策略
策略一是打桩,插桩代码,不影响软件的行为,提供对软件行为的深入了解
策略二:防狼围栏算法,思想类似二分查找,慢慢缩小范围定位即可
策略三是切片,比如某个值错了,然后就顺着跟这个值的计算相关的代码检查问题
策略四是利用版本控制,看哪一次版本t出现了问题,对比版本差异即可定位问题。如果版本列表比较长也可以采用二分查找
五、测试
测试在前面的博客已经说过,链接在此
在此就不再赘述。