免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
楼主: 77h2_eleven
打印 上一主题 下一主题

[函数] 大家在项目代码中会大量使用断言么?  关闭 [复制链接]

论坛徽章:
0
61 [报告]
发表于 2008-07-03 20:53 |只看该作者
原帖由 shan_ghost 于 2008-7-3 18:06 发表


因为工作环境不同。


一般来说,为国企(如电信、移动等巨头们)工作的都这样——我称之为“日志依赖症”和“崩溃恐惧症”。

进去做过就知道,他们是不会关心诸如“用户体验”之类垃圾的;关键是保证 ...



....poor! 原来是这样, 其实你只要用了pc-lint 你说的事情 就少了90%了

没有扎实的单元测试,小模块本身就有问题, 加在一起问题更多。 混在一起 问题更多更多, 但是这不意味着
日志做法就是推卸责任。  你对返回的结果不做处理才是推卸责任。 你任由错误的影响面不断扩大才是不负责任。

崩溃有多大的麻烦,  比如 批萨的订餐, 一个上海项目200-250人左右, 当出现一个问题, 你是让他当掉,然后下次来修改呢?
还是让这个电话断掉, 记个日志 以后来解决?   停一分钟 商家损失大概是1 w,  你当个程序,然后重启, 250个电话一起掉线,好吧准备出
事故报告吧。... 这些损失谁来负责?难不成兄弟 你准备掏腰包?

说我们的单元测试不过关, 好那 我告诉你 avaya 的交换机里的东西也有bug, 很不幸,我们还要顶着他的bug继续干活,
出问题了? 这个消息怎么会这样? 我们让他崩溃吧 然后慢慢来改。 如果客户同意, 我也愿意这么做。


话说的最后,就是 一个产品并没有经过非常严格的测试就上线了,结果有bug, 有环境,内部,系统本身造成的,为了避免
全部停摆,采用日志, 规避错误 来解决。 的确这不是个治本的办法。 但是 现在的项目速度快,很多时候没有条件
没有时间能完成这样的测试。我们不是做windows xx 可以出7个测试版。 所以一切都要从实际出发。

还有如果单靠断言, 那就等着 在客户那里崩溃,你这里怎么测也测不出来的情况出现吧。

论坛徽章:
0
62 [报告]
发表于 2008-07-03 21:02 |只看该作者
断言语句一般只是用来DEBUG的,我记得在发布的程序中用断言是不好的..

论坛徽章:
0
63 [报告]
发表于 2008-07-03 21:02 |只看该作者
单元测试里,这些特意设置的炸弹炸了几个,帮我纠正了几个bug;然后,即使我刻意给它扔各种各样的垃圾——刻意写错格式的xml;拿普通文本冒充 xml;读某个程序内部的二进制代码冒充xml;传入错误的、刻意把一些字段填写越界的结构体;甚至打开日志,随机读一段信息当参数往里乱扔,它都能准确报出错误而不崩溃。
之后,这个东西在很多地方重用,持续运行了半年,没有任何bug。

。。。。你这个就叫测试完成了吗?
没有!这样的测试,要完全覆盖 是非常困难的。你只能算是测试的次数多一点而已, 你测试的样本有没有代表性呢? 要完整测试这样的代码 是很困难的。

论坛徽章:
8
CU大牛徽章
日期:2013-04-17 10:59:39CU大牛徽章
日期:2013-04-17 11:01:45CU大牛徽章
日期:2013-04-17 11:02:15CU大牛徽章
日期:2013-04-17 11:02:36CU大牛徽章
日期:2013-04-17 11:02:58技术图书徽章
日期:2013-12-04 10:48:50酉鸡
日期:2014-01-03 10:32:30辰龙
日期:2014-03-06 15:04:07
64 [报告]
发表于 2008-07-03 21:42 |只看该作者
有没有工程经验,有没有真正负过责任,是看得出来的

何谓单元测试?

严格按编程规范来,一个函数不超过80~120行的话,隐藏错误的可能有多大?
一个头脑清晰的、亲手写出这区区100行代码的程序员,可能在单步执行每一行代码后,居然还看不出哪里和自己的设计思路不一致吗?
相信这么无能的家伙不会有多少吧。

如果函数本身可靠,十来个这种函数组成一个模块、且严格检查了出入参数,有错误可能测试不出来吗?



对,教科书上是写着“理论上不可能做到完全覆盖,测试永远不能证明代码绝对正确”……

但是,真的有必要做到完全覆盖吗?

有一点可以肯定: 根本不测试和测试不充分,情况是截然不同的。



另外: 程序员所说的“测试”,和测试工程师口中的“测试”,是截然不同的两回事。万不要使用测试工程师的观点对待身为程序员的你的单元测试任务。

举例来说: 没有任何一本教测试的教科书会提到“单步执行”,也没有任何一本教测试的书会告诉你如何使用watch和断点,更不会告诉你可以刻意制造崩溃以准确定位错误……

如果还把自己当作一个软件开发工程师,请抛弃头脑里那些流行于测试工程师群体的奇谈怪论——尤其是在您工程经验不足、还严重缺乏辨识能力的时候——虽然在测试者的世界里,它们确实是真理。




另外,你头脑里的容错和我的容错也是截然不同的。

比如我的xml解析器,如果你不放心,完全可以用try-catch保护起来——但其内部,我有信心;有错,就一定能准确识别。

至于最后的效果,正是我所举例的那个freeware老鸟的做法: find内部出错,catch之,走错误流程提示用户,然后恢复;反复出错则提交错误,再次恢复到正常工作中——这才是真正稳定的系统。



至于楼上这位先生,恕在下直言,您根本不懂什么叫容错。

客观世界里有很多异常,比如网络故障,比如硬盘满,比如未知的软件故障等等——这些都是应该走明确的异常流程的。


总体设计上,比如你们的电话接入模块,一个电话接入,走到某个流程上,遇到strcpy参数为null的现象: 这该如何处理?

显然,从阁下的言论里,你们是让它返回错误,然后记日志,走错误流程,继续。

但问题是: 你们的strcpy报的是arg_error。于是后面的流程不会知道这是指针为空(以及这个问题所隐含表示的更大的逻辑缺陷);或者虽然知道,但会在进一步传递中逐渐扭曲、丢失;最终,问题同样会爆,但这次爆的是更大的逻辑缺陷所造成的更为隐晦的古怪问题
说不定,你们跑飞的代码会把硬盘给格掉——内存越界,栈被破坏时,谁知道会执行到什么地方。

现在,请问: 这个损失谁来负责?阁下吗?下次的系统设计,您凭什么还想拿到手?


相反,在下一直强调发现错误就让它爆掉,这有什么好处?
好处就是:strcpy会因为引用null而在用户那里崩溃,但这个错误不会造成任何其他潜在的破坏。

由于整个流程上有机制在保护——比如,发行版中,我的try_catch块会捕获电话接入流程的所有异常;启动错误日志记录之,然后断掉这个电话或提示对方重新开始。所以我的设计当然要比你们隐藏错误的设计更稳定,用户体验更好

于是,在下的设计:
1、不造成更大故障
2、立刻捕获错误,避免错误扩散后难于定位——也许你们还在通宵查服务器硬盘被格式化的原因,我们第10个新版本已经上线了
3、用户体验好——对了,他必定得到正确结果;错了,立刻报告错误并提请重试:总之,绝对不会出现面条系统里面某些信息莫名其妙丢失却总也找不到原因的下三滥问题。

[ 本帖最后由 shan_ghost 于 2008-7-3 23:22 编辑 ]

论坛徽章:
0
65 [报告]
发表于 2008-07-03 21:53 |只看该作者
NNGX,还在加班!
上来看看这个帖子依然火。申请版主给加个精吧。
多好的帖子~~~~~~~~

论坛徽章:
8
CU大牛徽章
日期:2013-04-17 10:59:39CU大牛徽章
日期:2013-04-17 11:01:45CU大牛徽章
日期:2013-04-17 11:02:15CU大牛徽章
日期:2013-04-17 11:02:36CU大牛徽章
日期:2013-04-17 11:02:58技术图书徽章
日期:2013-12-04 10:48:50酉鸡
日期:2014-01-03 10:32:30辰龙
日期:2014-03-06 15:04:07
66 [报告]
发表于 2008-07-03 22:59 |只看该作者
try..catch是c++新引入的东西——c中可与之对应的是setjmp/longjmp;较近的设计较好的系统会把它们用宏包装成try...catch——这些相对生僻的东西很多人可能不知道,或者因为缺乏工程实践经验而不知该如何使用。

异常和assert是两种不同的东西——这是我一开始就提到过的;只是当时不想讨论的过于深入罢了。



明确如下几点即可:
1、可以预见到的错误(如磁盘满、网络出错等等)应该使用代码侦测、识别并作出处理;
2、写好、测好你的每一个函数;用assert检查不应该发生的情况,强迫使用你的代码的人修正他们的逻辑;
3、发现错误,只要不能明确它的发生原因或影响范围,就应该毫不含糊地抛异常——如果你们的系统支持异常或者经理对异常不是特别反感的话;否则,像我一样刻意制造个除零错,让被除数标识错误信息也行;
   无论如何,绝不要把不能准确识别的东西当作普通错误
4、在模块级恰当使用try...catch可以显著提高系统稳定性,并且不会造成性能问题;但这种做法有可能掩盖错误。
5、使用不当的try..catch会拖慢系统性能,也可能导致代码执行逻辑混乱,必须审慎对待。
6、你不会catch库函数,因为它们太可靠了,以至于发生所有错误几乎100%都是你犯了错;同样,一个封装的极其漂亮、被多次复用证实可靠的模块同样不需要catch——打个广告,比如我的xml模块^_^——那么,通过抽象业务逻辑,写出类似的可复用代码,形成公司自己的开发库之后,你会发现你们的开发方式会迅速向RAD方式靠拢,以至于为另一个客户做出另外一个表面上相当不同的系统只需要3天,而且基本没有bug。
    ——真正好的软件公司,一般都会有这样的基础库(我现在的项目经理就正在抓这个);看看暴雪的war 3,想想支撑他们剧情脚本的是什么;看看war 3的可执行文件有多大;再看看网上流传的无数的war 3 RPG地图: 告诉我,他们怎么能把程序做的如此稳定?

论坛徽章:
0
67 [报告]
发表于 2008-07-03 23:07 |只看该作者
严格按编程规范来,一个函数不超过80~120行的话,隐藏错误的可能有多大?
一个头脑清晰的、亲手写出这区区100行代码的程序员,可能在单步执行每一行代码后,居然还看不出哪里和自己的设计思路不一致吗?
相信这么无能的家伙不会有多少吧。

一个头脑清晰的、亲手写出这区区100行代码的程序员,可能在单步执行每一行代码后,居然还看不出哪里和自己的设计思路不一致吗?

我只能说 自信吧,  那么我想请问阁下, 阁下的每个函数都100来行, 所有的程序都是没有bug的。
那么世界上大把符合您要的代码还有大把的bug,





如果函数本身可靠,十来个这种函数组成一个模块、且严格检查了出入参数,有错误可能测试不出来吗?

那么我想请问你 apache postfix 这样的完全符合您要求的开源著名产品,怎么还是有bug啊?
他们怎么搞的?  

linux 内核0.11 里面有个bug 4年后被发现, 上次unix 的一个目录bug 22年才被发现, 看来
阁下应该多花点时间,帮助一下这个落后的开源体系了。


对,教科书上是写着“理论上不可能做到完全覆盖,测试永远不能证明代码绝对正确”……

但是,真的有必要做到完全覆盖吗?

有一点可以肯定: 根本不测试和测试不充分,情况是截然不同的。

第一  我从未说过 根本不测试,  
第二 以你的例子来看,你只是盲目乐观,仅此而已。或许在99%的情况下是好的。你在try catch里面保护。 只不过是依赖操作系统给你最后一根救命稻草。  要知道 c 语言里 没有这样的救命稻草。
本质没有任何区别。




另外,你头脑里的容错和我的容错也是截然不同的。

比如我的xml解析器,如果你不放心,完全可以用try-catch保护起来——但其内部,我有信心;有错,就一定能准确识别。

你的信心 只对你自己有用。  毫无意义。



但问题是: 你们的strcpy报的是arg_error。于是后面的流程不会知道这是指针为空(以及这个问题所隐含表示的更大的逻辑缺陷);或者虽然知道,但会在进一步传递中逐渐扭曲、丢失;最终,问题同样会爆,但这次爆的是更大的逻辑缺陷所造成的更为隐晦的古怪问题

后面的流程 会按照出错的方法来解决, 就像你的try catch 捕捉到一样。
如果你try catch 到以后,你会仍然按照原来的流程走下吗?你会吗? 你不会, 你会去处理这个异常。
一样 后面流程看到前面的返回错误了, 就会走错误处理的流程。


由于整个流程上有机制在保护——比如,发行版中,我的try_catch块会捕获电话接入流程的所有异常;启动错误日志记录之,然后断掉这个电话或提示对方重新开始。所以我的设计当然要比你们隐藏错误的设计更稳定,用户体验更好

你有没有试过,遇到错误,但是异常没有跑出来的情况呢? 就像strncpy( 的dest * 为NULL. 它执行了,它没有崩溃, 它也没有异常,但是它错误了。  如果你使用过 录音卡 如 ipm 这类板块,你就会知道。 它的系统运行在独立的板块上, 它未必每次工作都是稳定的。都能把异常抛出来。
因为别人本来就是 给多种系统设计的,为了保证各个平台的统一性,它一定会牺牲一些东西,它不可能保证每个系统都有异常。 如果你希望依靠异常来解决,OK 请便吧。


也许你们还在通宵查服务器硬盘被格式化的原因,我们第10个新版本已经上线了
不用了, 第二个版本 的崩溃 你们就已经被使用商退回来了。你们已经out!

用户体验好——对了,他必定得到正确结果;错了,立刻报告错误并提请重试:总之,绝对不会出现面条系统里面某些信息莫名其妙丢失却总也找不到原因的下三滥问题

错误了
某些信息莫名其妙丢失却总也找不到原因的。 那只是你的代码水平问题。 要知道 不是每个工程师都对错误视而不见。
就好象苏联的机器 虽然笨重 但是工作起来是很稳定的。和所谓的设计方法无关。

论坛徽章:
8
CU大牛徽章
日期:2013-04-17 10:59:39CU大牛徽章
日期:2013-04-17 11:01:45CU大牛徽章
日期:2013-04-17 11:02:15CU大牛徽章
日期:2013-04-17 11:02:36CU大牛徽章
日期:2013-04-17 11:02:58技术图书徽章
日期:2013-12-04 10:48:50酉鸡
日期:2014-01-03 10:32:30辰龙
日期:2014-03-06 15:04:07
68 [报告]
发表于 2008-07-03 23:48 |只看该作者
但问题是: 你们的strcpy报的是arg_error。于是后面的流程不会知道这是指针为空(以及这个问题所隐含表示的更大的逻辑缺陷);或者虽然知道,但会在进一步传递中逐渐扭曲、丢失;最终,问题同样会爆,但这次爆的是更大的逻辑缺陷所造成的更为隐晦的古怪问题

后面的流程 会按照出错的方法来解决, 就像你的try catch 捕捉到一样。
//不好意思,您处理不了。爆了,说明是严重bug,后面catch到可以防止带病运行;不爆,必定要带病运行。正是这个带病运行会格用户的硬盘。
//请不要再在这个问题上掺杂不清。说车轱辘话的人很讨厌。



要知道 c 语言里 没有这样的救命稻草。
这句话再写一次:
try..catch是c++新引入的东西——c中可与之对应的是setjmp/longjmp;较近的设计较好的系统会把它们用宏包装成try...catch——这些相对生僻的东西很多人可能不知道,或者因为缺乏工程实践经验而不知该如何使用。


你有没有试过,遇到错误,但是异常没有跑出来的情况呢? 就像strncpy( 的dest * 为NULL. 它执行了,它没有崩溃, 它也没有异常,但是它错误了。  如果你使用过 录音卡 如 ipm 这类板块,你就会知道。 它的系统运行在独立的板块上, 它未必每次工作都是稳定的。都能把异常抛出来。
//这是特殊平台的特殊问题,请别拿到普遍问题讨论里来搅和。这并不能显示您的水平。
//真想玩的话,CU首页有个brian fuck机,在那上面写程序更好玩。


不用了, 第二个版本 的崩溃 你们就已经被使用商退回来了。你们已经out!
//不好意思,即使catch没有起到作用,我们仍然没有让用户损失数据;倒是格了用户硬盘的你们,还正忙着打官司,正在公证人的监视下,让数据恢复公司提取硬盘数据取证呢。
//同时,由于被用户控告在代码中嵌入病毒,公司正雇人调查您们项目组的全体工作人员,看谁平常牢骚最多呢



错误了
某些信息莫名其妙丢失却总也找不到原因的。 那只是你的代码水平问题。 要知道 不是每个工程师都对错误视而不见。

//很不幸,把非法的空指针参数转换成莫名其妙的arg_error的strcpy函数,正是阁下力挺的杰作;反倒是在下一直在讨伐这种代码




最后,希望这点东西能治好你的测试恐惧症:

程序员所说的“测试”,和测试工程师口中的“测试”,是截然不同的两回事。万不要使用测试工程师的观点对待身为程序员的你的单元测试任务。

举例来说: 没有任何一本教测试的教科书会提到“单步执行”,也没有任何一本教测试的书会告诉你如何使用watch和断点,更不会告诉你可以刻意制造崩溃以准确定位错误……

如果还把自己当作一个软件开发工程师,请抛弃头脑里那些流行于测试工程师群体的奇谈怪论——尤其是在您工程经验不足、还严重缺乏辨识能力的时候——虽然在测试者的世界里,它们确实是真理。



[ 本帖最后由 shan_ghost 于 2008-7-3 23:51 编辑 ]

论坛徽章:
0
69 [报告]
发表于 2008-07-04 00:14 |只看该作者
不要再争了,断言替代不了异常处理,两个责任根本不同

[ 本帖最后由 narkissos 于 2008-7-4 00:36 编辑 ]

论坛徽章:
8
CU大牛徽章
日期:2013-04-17 10:59:39CU大牛徽章
日期:2013-04-17 11:01:45CU大牛徽章
日期:2013-04-17 11:02:15CU大牛徽章
日期:2013-04-17 11:02:36CU大牛徽章
日期:2013-04-17 11:02:58技术图书徽章
日期:2013-12-04 10:48:50酉鸡
日期:2014-01-03 10:32:30辰龙
日期:2014-03-06 15:04:07
70 [报告]
发表于 2008-07-04 01:24 |只看该作者
原帖由 narkissos 于 2008-7-4 00:14 发表
断言在测试覆盖100%正常和异常分支的时候有意义。但是:
1.没有能100%覆盖的用例,异常永远是难于想像的。如果靠这个来替代抓异常、记log,是绝对不行的,而且release下也没有这东西。
2.既然1,那么在releas ...



只能说你还是根本就没弄明白assert究竟是干什么的。



在低级层面上,异常可能确实“永远难于想像”;但在设计层面上,你必须正确对待所有异常。


正因为测试覆盖不可能达到100%,所以代码总是可能有bug;但这种代码充斥bug的现象绝对不能成为永久状况

那么,怎么才能把程序写好?

答案就是: 不能有半点含糊。


如果一个错误可以预料,那么用代码处理之;如果某个设备极不稳定,那么想办法校验之——这些,都是运行时逻辑层面上的东西,是总体设计层面必须考虑到的问题。

同样,代码可能有bug,那么有bug的这块代码就完全等同于一个不稳定的设备,真正要做的就是想办法精确侦测到它什么时候出错了,然后想办法恢复执行。

这些,不用废话,当然还是总体设计层面上的问题。



比如,一个分布式网格计算系统里,联网的某台机器内存条老化损坏,总输出垃圾,怎么办?

正确做法显然是: 剔除这台机器,把它的工作移交给其他机器做;勉强它做下去,后果绝对不是你所想要的。

异常的作用,就是检查并剔除这样的垃圾设备;其他乱七八糟的所谓记日志继续、放弃继续等等办法,都是得不偿失的。
——一台内存已经损坏的机器,希望它能自我恢复,可能吗?除了造成更多、更大的灾难,还有什么意义?


那么,断言是做什么的?

它的作用是: 在测试期就找到并剔除这样的机器。
如果此时没找到,就只能寄希望于异常了。




站在设计师的角度看问题,一个个的函数、模块就是这样一台台随时可能出问题的机器。
关键就在于: 只有正常工作的机器,它们的报告才是可信的;出了问题的机器必须确认问题原因不会影响它的可信度,才能决定它是否应被屏蔽掉。

还拿strcpy这个例子来说,在下一直强调要用assert检查指针为null、强调发生这种情况一定要让它爆出来,就是因为包含strcpy的那个函数/模块已经不可信了——不然它不会传一个null给strcpy。
在设计师眼里,这个模块(的此次执行)就是坏了内存的那台机器。所以当前执行过程必须中止,从更高的层面上试图恢复正常运行。
如果不剔除这台机器,它肯定会造成更多五花八门的错误。

正因此,在下一直强调精确。
只有你精确识别了错误,才应该自己想办法让自己的当前模块恢复运行;任何不能精确界定原因的错误——不管它是空指针还是类似arg_error的错误返回——都必须立刻停止运行,让高层模块替换掉你(对程序来说,就是此次调用必须无条件撤销,不能带病继续执行了)。

正因为给stycpy传空指针已经足以说明模块其他部分已经出错了,绝对不可信任了,所以此时必须有一个异常——不管这个异常是assert还是throw得到的——总之,必须给我抛出来。
不同点是: 使用assert的显然是对自己公司的流程规范有信心,相信这种错误一定能在测试期被发现;使用throw则相对要欠缺信心,以至于要消耗运行期执行效率来侦测错误。

相信传给strcpy空指针的模块返回的错误描述,无异于要求我们相信疯子对自己当前精神状态的判定。

如果一个设计师会轻易相信疯子的话,恐怕他自己也离疯不远了。


因为世界并不完美,虽然基于良好设计的系统肯定可以使用throw和try...catch保证运行状态可靠,但它对资源的消耗太大。
那么,界定出可信任的代码,免除它们中间的throw和try...catch,显然可以大幅度提高运行效率,又不会显著降低系统的可靠性。
而在可信任代码和不可信任代码的边界,就可以用assert在测试期查找“脏”信息,勒令犯错的不可信任代码修改逻辑,提高它们的可靠性。
不可信任代码的可靠性,则仍然必须消耗运行时效率的try...catch来保证。




现在,我们可以给面条代码一个清晰的定义了:
所谓面条代码,是一种在没有清晰的整体观、大局观的设计师和程序员的共同努力下,写出来的死皮赖脸都要继续运行下去的代码。
这种代码的特点就是糊涂、冗长:它的每一个函数、每一个模块都竭尽全力的想要捕获所有异常,却弄不明白大部分错误之所以发生的精确原因;这种稀里糊涂的故障会被写到一本叫做日志的糊涂账里,然后装作若无其事的样子继续执行——但却永远不能确定错误是否已经真正被精确识别和隔离。
未被隔离的异常会在代码中四处流窜,然后大部分都会莫名其妙的销声匿迹,只有少部分会以一种极其诡异的方式爆发出来——很多情况下,这种错误无法找到准确原因;为避免再次爆发,代码里会被添加更多的捕获异常的代码(这些代码通常会导致更多稀里糊涂的故障,需要冗长的测试过程去排除),然后把捕捉到的一切统统写入 日志.糊涂账.big 中(运气好的话,也许能碰巧抓到那个关键的炸弹包)。

[ 本帖最后由 shan_ghost 于 2008-7-4 01:58 编辑 ]
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP