关于异常的最终讨论

45 views
Skip to first unread message

pongba

unread,
Oct 29, 2007, 1:39:57 AM10/29/07
to pon...@googlegroups.com

标题党了一下:P 

根据上次的讨论中大家的意见,我将异常的优势分类,有一些是人力问题,有一些是细节问题,有一些是工具可以解决的问题,剩下是根本问题。目的是要筛选出根本问题。然后看看异常究竟有什么根本性优势。

 

然后我总结一下倾向于错误代码一边的观点。并将其与异常的根本优势作一个对照,大家看看能得出什么结论。

 

以下是异常的优势(我在每一点后面都加了括号,说明是哪类问题)。

 

1.       麻烦。每一个可能返回错误代码的调用边界都需要检查,不管你实际上对不对返回的错误作响应,因为即便你自己不解决返回的错误,也要把它传播到上层去好让上层解决。 (人力问题/假定存在工具可以解决)

2.       不优雅且不可伸缩(scalability) 的代码(错误处理代码跟One True Path(也叫 happy path搅和在一起)。(人力问题)

3.       脆弱(易错)。只要忘了检查任意一个错误代码,执行流就必然会带着错误状态往下继续执行,后者几乎肯定不是你想要的。带着错误状态往下执行好一点的会立即崩溃,差一点的则在相差十万八千里的地方引发一个莫名其妙的错误。 (假定存在工具可以检查)

4.       难以(编写时)确保和(review 时)检查代码的正确性。需要检查所有可能的错误代码有没有都被妥善check 了,其中也许大部分都是不能直接对付而需要传播给上级的错误。(假定存在工具可以检查)

5.       耦合即便你的函数是一个异常中立的函数,不管底层传上来哪些错误一律抛给上层,你仍然需要在每个调用的边界检查,并妥善往上手动传播每一个错误代码。而一旦底层接口增加、减少或改动错误代码的话,你的函数就需要立即作出相应改动,检查并传播底层接口改动后相应的错误代码—— 这是很不幸的,因为你的函数只是想保持异常中立,不管底层出什么错一律抛给上层调用方,这种情况下理想情况应该是不管底层的错误语意如何修改,当前层都应该不需要改动才对(人力问题)

6.       没有异常,根本无法编写泛型组件。泛型组件根本不知道底层会出哪些错,泛型组件的特点之一便是错误中立。但用 error-code的话,怎么做到错误中立?泛型组件该如何检查,检查哪些底层错误?唯一的办法就是让所有的底层错误都用统一的SUCCEEDEDFAILED代码来表达,并且将其它错误信息用 GetLastError来获取。姑且不说这个方案的丑陋,如何、由谁来统一制定SUCCEEDEDFAILEDGetLastError 的标准?就算有这个统一标准,你也可以设想一下某个标准库泛型算法(如for_each )编写起来该是如何丑陋。(根本问题)

7.       错误代码不可以被忘掉(忽视)。忘掉的后果见第 3条。此外,有时候我们可能会故意不管某些错误,并用一个万能catch来捕获所有未被捕获的错误, log,向支持网站发送错误报告,并重启程序。用异常这就很容易做到——只要写一个 unhandled exception handler(不同语言对此的支持机制不一样)即可。(假定存在工具可以检查)

 

异常与错误代码的本质区别之二——异常的传播使用的是一个单独的信道,而错误代码则占用了函数的返回值;函数的返回值本来的语意是用来返回"有用的"结果的,这个结果是属于程序的 One True Path的,而不是用来返回错误的。

 

利用返回值来传播错误导致的问题如下:

 

8.       所有函数都必须将返回值预留给错误。如果你的函数最自然的语意是返回一个 double,而每个double都是有效的。不行,你得把这个返回值通道预留着给错误处理用。你可能会说,我的函数很简单,不会出错。但如果以后你修改了之后,函数复杂了呢?到那个时候再把返回的 double改为int 并加上一个double&作为 out参数的话,改动可就大了。(细节问题/ 人力问题)

9.       返回值所能承载的错误信息是有限的 NULL-1?什么意思?具体的错误信息只能用 GetLastError来提供哦,对了,你看见有多少人用过 GetLastError的?(人力问题)

10.   不优雅的代码。呃这个问题前面不是说过了么?不,这里说的是另一个不优雅之处——占用了用来返回结果的返回值通道。本来很自然的"计算——返回结果",变成了"计算——修改out 参数——返回错误"。当然,你可以说这个问题不是很严重。的确,将double res = readInput();改为double res; readInput(&res); 也没什么大不了的。如果是连调用呢?比如,process(readInput());或者readInput() + …?或者一般地, op1(op2(), op3(), …);(细节问题/ 人力问题)

11.   错误汇报方案不一致性。看看 Win32下面的错误汇报机制吧:HRESULT BOOLGetLastError…本质上就是因为利用返回值通道是一个补丁方案,错误处理是程序的一个方面( aspect),理应有其单独的汇报通道。利用异常的话,错误汇报方案就立即统一了,因为这是一个 first-class的语言级支持机制。(人力问题)

12.   有些函数根本无法返回值,如构造函数。有些函数返回值是语言限制好了的,如重载的操作符和隐式转换函数。(根本问题 /细节问题)

 

异常与错误代码的本质区别之三——异常本身能够携带任意丰富的信息

 

13.   有什么错误报告机制能比错误报告本身就包含尽量丰富的信息更好的呢?使用异常的话,你可以往异常类里面添加数据成员,添加成员函数,携带任意的信息(比如 Java的异常类就缺省携带了非常有用的调用栈信息)。而错误代码就只有一个单薄的数字或字符串,要携带其它信息只能另外存在其它地方,并期望你能通过GetLastError 去查看。(根本问题/人力问题)

 

异常与错误代码的本质区别之四——异常是OO

 

14.   你可以设计自己的异常继承体系。呃那这又有什么用呢?当然有用,一个最大的好处就是你可以在任意抽象层次上catch 一组异常(exception grouping),比如你可以用 catch(IOException)来捕获所有的IO异常,用 catch(SQLException)来捕获所有的 SQL异常。用catch(FileException) catch所有的文件异常。你也可以catch更明确一点的异常,如 StreamEndException。总之,catch 的粒度是粗是细,根据需要,随你调节。当然了,你可以设计自己的新异常。能够catch 一组相关异常的好处就是你可以很方便的对他们做统一的处理。(根本问题)

 

异常与错误代码的本质区别之五——异常是强类型的

 

15.   异常是强类型的。在 catch异常的时候,一个特定类型的catch只能 catch类型匹配的异常。而用error-code的话,就跟 enum一样,类型不安全。-1 == foo() FAILED == foo() MB_OK == foo()?大家反正都是整数。(细节问题)

 

异常与错误代码的本质区别之六——异常是first-class 的语言机制

 

16.   代码分析工具可以识别出异常并进行各种监测或分析。比如 PerfMon就会对程序中的异常做统计。这个好处放在未来时态下或许怎么都不应该小觑。(未知问题)

 

综上,真正根本的问题有:

6. 没有异常,根本无法编写泛型组件。

14. 你可以设计自己的异常继承体系。

16. 代码分析工具可以识别出异常并进行各种监测或分析。

 

对于第6点,如果你的项目中不需要编写泛型组件,那么这一点约束便不存在。

14点,似乎在实际项目中也没那么关键。其实也可以归结为人力问题。

16点的优势目前还不明确。

 

总结:

因此,看上去,异常的缺点是,需要学习掌握一门新的机制(而且并不容易),优点是能够使错误处理代码变得很优雅,能够省掉许多人力。而错误处理代码的优点是每个人都会,只需要强制执行编码规范和使用工具(可以假定存在这样的工具,因为如果不存在的话,异常的 N多优点就又都浮现出来了),也可以确保错误安全。缺点正对应于异常的优点。

 

结论似乎很明显:使用异常,由于没有合格的开发人员,所以异常的优点甚至没有发挥的机会,结论就是事与愿违。而使用错误代码,每个人都知道怎么做,只要加以严格规范,和使用工具(如果有的话),虽然代码丑陋,但还是能够做到错误安全的。

 

现实问题:

1. 存在检查是否check了每个可能的错误返回值的工具吗?

2. 如果存在,你的项目用了吗?进一步,有多少现实的项目用了呢?(我知道在Joel 的项目中是通过编码规范来约束开发人员"一定要check所有返回值"的)。

3. 通过编码规范来约束,可靠吗?(或许总比依赖于程序员对异常这一语言特性的掌握程度可靠?)

4. 如果没有这样的工具,在未来可能出现这样的工具吗?如果存在,可能普及吗?(我的认为是不可能,因为异常在目前的所有主流语言中都是唯一的正规军)。

5. 掌握异常这一语言真的很困难吗?"没有合格的开发人员"的问题的根源是什么?是因为这一语言特性真的很困难?还是其它什么原因。

 

最后:

如果你是项目主管,你是愿意在项目的一开始花钱培训程序员,教会他们如何正确使用异常。还是愿意在项目的整个过程中花钱(人力资源也是钱),以使得他们的确正确使用了错误代码。这是一个决策问题。给出你的决策,并给出决策的经济学理由(不是主观理由),如果你选择 A,那么为什么你觉得A最经济?反之亦然。

wang xin

unread,
Oct 29, 2007, 2:05:19 AM10/29/07
to pon...@googlegroups.com


在07-10-29,pongba <pon...@gmail.com> 写道:

现实问题:

1. 存在检查是否check了每个可能的错误返回值的工具吗?


不知道,希望有

2. 如果存在,你的项目用了吗?进一步,有多少现实的项目用了呢?(我知道在Joel 的项目中是通过编码规范来约束开发人员"一定要check所有返回值"的)。


因为1的不确定性答案,no

3. 通过编码规范来约束,可靠吗?(或许总比依赖于程序员对异常这一语言特性的掌握程度可靠?)


可靠,因为我们这边有着极其严格的code review过程(顺便说一下,获得高质量代码的最佳途径就是code review,其他的都是纸上谈兵)

4. 如果没有这样的工具,在未来可能出现这样的工具吗?如果存在,可能普及吗?(我的认为是不可能,因为异常在目前的所有主流语言中都是唯一的正规军)。


我想会的,因为从技术角度来看难度不大;但能不能广为流传,就不一定了;最有可能就是,某小牛在自己的team里面写了一个给大伙用

5. 掌握异常这一语言真的很困难吗?"没有合格的开发人员"的问题的根源是什么?是因为这一语言特性真的很困难?还是其它什么原因。


真的很难 ,"没有合格的开发人员"的问题根源就是,CS作为一门新兴的科学,没有足够的底蕴来制造出现在IT行业所需要的所有从业人员;语言特性的使用很简单,但细节太多太零碎,导致用错的概率近乎100%

最后:

如果你是项目主管,你是愿意在项目的一开始花钱培训程序员,教会他们如何正确使用异常。还是愿意在项目的整个过程中花钱(人力资源也是钱),以使得他们的确正确使用了错误代码。这是一个决策问题。给出你的决策,并给出决策的经济学理由(不是主观理由),如果你选择 A,那么为什么你觉得A最经济?反之亦然。


好吧,我选择做一个反面人物,我不愿意花钱培训程序员,也不愿意在项目中花钱让他们成长;培训这个过程应该在学校里面就结束了,工作中能够给以的培训只能是很有限的。换而言之,我希望可以招到熟练工,如果招不到,那么就希望找几个机灵点的,可以在平时项目过程中被"潜移默化"的;如果让我专门花钱来培训他们,No  Way !!!!!





--
Everything is possible and available if we trust ourselves!

莫华枫

unread,
Oct 29, 2007, 2:11:40 AM10/29/07
to pon...@googlegroups.com
异常还有一个优势:可以在构造函数和操作符重载中使用。
mfc很遭人诟病的一个问题就是两阶段构造:
CWindow  wnd;
bool res=wnd.Create(...);
不但有碍观瞻,而且容易造成错误。

在07-10-29,pongba < pon...@gmail.com> 写道:



--
反者道之动,弱者道之用
m...@seaskysh.com
longsh...@gmail.com
http://blog.csdn.net/longshanks/

pongba

unread,
Oct 29, 2007, 2:13:29 AM10/29/07
to pon...@googlegroups.com
All sounds reasonable:-)

只有一个问题:review过程也要花钱的,对不?具体地说,使用错误代码作为机制,那么有相当一部分review工作要花在"检查所有函数的所有返回值是否都被check了",具体多少工作量我不知道。此外,考虑到代码修改过程中引入的问题:比如一个被调用的函数增加了一个错误返回代码,那么所有的调用方都需要重新review一下,不管调用方是否是错误中立的。这是事实吧?但这个问题到底要紧到什么程度,我还是不知道。但有一点是肯定的,归根到底这是钱的问题。
那么既然是钱的问题,我就不禁要问了。培训人员也是钱的问题,对不?如果一开始的培训能够省掉过程中不必要的review部分(像我上面的提到的,显然,用异常跟用错误代码,需要的review量是不一样的)。那么哪个更经济呢?
如果认为在一开始培训人员不经济,那么为什么不?是因为难度太大?为什么大?要specific的理由。主观的倾向还不够strong:-)
--
刘未鹏(pongba)|C++的罗浮宫
http://blog.csdn.net/pongba
TopLanguage
http://groups.google.com/group/pongba

pongba

unread,
Oct 29, 2007, 2:16:49 AM10/29/07
to pon...@googlegroups.com
这个相比于其它问题,我倒是认为可以归为细枝末节问题。
忽视返回值check导致的灾难才是真正重要的问题,需要人力(钱)来ensure。相较之下小小的构造函数调用,即便awkward,so what?比起错误代码导致的其它不优雅,我觉得就不值一提了。

莫华枫

unread,
Oct 29, 2007, 2:26:27 AM10/29/07
to pon...@googlegroups.com
好吧,我选择做一个反面人物,我不愿意花钱培训程序员,也不愿意在项目中花钱让他们成长;培训这个过程应该在学校里面就结束了,工作中能够给以的培训只能是很有限的。换而言之,我希望可以招到熟练工,如果招不到,那么就希望找几个机灵点的,可以在平时项目过程中被"潜移默化"的;如果让我专门花钱来培训他们,No  Way !!!!!

本想抨击一下现行的教育(我最喜欢做的事:)),想想算了,说了也白说。
不过,培训有时不一定要很多钱,"潜移默化"也可以算是一种培训。
我有这么一个故事,是真的:
我们一个程序员学习了一些多层模型的知识,建立了一套多层模型的设计样本,在公司里使用。由于起初建立在.net1.1上,没有强类型容器,只得为每个业务对象类提供一个独立xxxList类。 到了.net2.0之后,我在一次技术交流会上讲解了泛型,提到可以用泛型解决这类问题。他立刻明白了,修改了模型,消减了1/3的类。之后,他又用泛型创造了一个业务类的基础类,大幅消减了重复代码。
我只一句话,便使得他了解、熟悉,直至使用一种从未用过的特性。因为他看到了好处。

wang xin

unread,
Oct 29, 2007, 2:26:29 AM10/29/07
to pon...@googlegroups.com


在07-10-29,pongba <pon...@gmail.com> 写道:
All sounds reasonable:-)

只有一个问题:review过程也要花钱的,对不?具体地说,使用错误代码作为机制,那么有相当一部分review工作要花在"检查所有函数的所有返回值是否都被check了",具体多少工作量我不知道。此外,考虑到代码修改过程中引入的问题:比如一个被调用的函数增加了一个错误返回代码,那么所有的调用方都需要重新review一下,不管调用方是否是错误中立的。这是事实吧?但这个问题到底要紧到什么程度,我还是不知道。但有一点是肯定的,归根到底这是钱的问题。

review的目的不是为了专门去"检查所有函数的所有返回值是否都被check了",有时是看你的编码风格有没有问题,有时是要知道你的程序的允许逻 辑,还有时就是看看你的实现和设计有没有偏差;而且,review还可以做到一个backup的作用,这样,即便一个重要模块的owner闪人了,也可以 找到其他人来维护它;从我目前的经验来看,review的工作量实际上不是很大的(当然,一开始还是感觉很大的)。
添加一个错误返回代码是属于一个设计上的问题,这一点应该在设计阶段就被提出的,和code review关系不大;不是说,你想添加一个error code就可以添加一个的。一旦你在设计过程中把这个问题raise up,那么在设计文档中就应该指出这种改变大致会影响到那些地方,从而指导implementor去修改这些地方

听上去貌似一个很落伍且效率很低下的流程吧:(
 

那么既然是钱的问题,我就不禁要问了。培训人员也是钱的问题,对不?如果一开始的培训能够省掉过程中不必要的review部分(像我上面的提到的,显然,用异常跟用错误代码,需要的review量是不一样的)。那么哪个更经济呢?
如果认为在一开始培训人员不经济,那么为什么不?是因为难度太大?为什么大?要specific的理由。主观的倾向还不够strong:-)

review绝对不是可以省略掉的那种"不必要"的部分;另外,"钱"不会是问题,问题是,我们需要的人才不是说需要经过专门的培训才可以成长为"人才"的人。这个问题已经扯的足够远了,已经和"异常VS. Error Code"无关了;唯一的关系就是,我不觉得那些需要我们对他们进行培训异常和error code之间的优劣的人,会是我们所期望找的人

Googol Lee

unread,
Oct 29, 2007, 2:52:08 AM10/29/07
to TopLanguage
如果是个项目一开始,估计还是会用异常的(还要保证依赖的库能对异常友好。在c#,java上都不是问题,c++就难说了)。不过对于现有项目(大家大
部分时间都是在现有项目上增增补补吧?包括在原有系统上增加新功能也属于这种情况),想用异常比较难,最起码的,抛了之后,谁接收?

On Oct 29, 1:39 pm, pongba <pon...@gmail.com> wrote:
> 标题党了一下:P
>
> 根据上次的讨论中大家的意见,我将异常的优势分类,有一些是人力问题,有一些是细节问题,有一些是工具可以解决的问题,剩下是根本问题。目的是要筛选出根本问题。然后看看异常究竟有什么根本性优势。
>
> 然后我总结一下倾向于错误代码一边的观点。并将其与异常的根本优势作一个对照,大家看看能得出什么结论。
>
> 以下是异常的优势(我在每一点后面都加了括号,说明是哪类问题)。
>

> 1. *麻烦*
> 。每一个可能返回错误代码的调用边界都需要检查,不管你实际上对不对返回的错误作响应,因为即便你自己不解决返回的错误,也要把它传播到上层去好让上层解决。*
> (人力问题**/**假定存在工具可以解决)*
>
> 2. *不优雅且不可伸缩**(scalability)**的代码*(错误处理代码跟One True Path(也叫happy path)
> 搅和在一起)。*(人力问题)*
>
> 3. *脆弱(易错)*


> 。只要忘了检查任意一个错误代码,执行流就必然会带着错误状态往下继续执行,后者几乎肯定不是你想要的。带着错误状态往下执行好一点的会立即崩溃,差一点的则在相差十万八千里的地方引发一个莫名其妙的错误。

> *(假定存在工具可以检查)*
>
> 4. *难以(编写时)确保和(**review**时)检查代码的正确性*。需要检查所有可能的错误代码有没有都被妥善check
> 了,其中也许大部分都是不能直接对付而需要传播给上级的错误。*(假定存在工具可以检查)*
>
> 5. *耦合*。
> 即便你的函数是一个异常中立的函数,不管底层传上来哪些错误一律抛给上层,你仍然需要在每个调用的边界检查,并妥善往上手动传播每一个错误代码。而一旦底层接口增加、减少或改动错误代码的话,你的函数就需要立即作出相应改动,检查并传播底层接口改动后相应的错误代码--
> *这是很不幸的*,因为你的函数只是想保持异常中立,不管底层出什么错一律抛给上层调用方,这种情况下*
> 理想情况应该是不管底层的错误语意如何修改,当前层都应该不需要改动才对*。*(人力问题)*
>
> 6. *没有异常,根本无法编写泛型组件*。泛型组件根本不知道底层会出哪些错,泛型组件的特点之一便是错误中立。但用error-code


> 的话,怎么做到错误中立?泛型组件该如何检查,检查哪些底层错误?唯一的办法就是让所有的底层错误都用统一的SUCCEEDED和FAILED
> 代码来表达,并且将其它错误信息用GetLastError来获取。姑且不说这个方案的丑陋,如何、由谁来统一制定SUCCEEDED和FAILED、

> GetLastError的标准?就算有这个统一标准,你也可以设想一下某个标准库泛型算法(如for_each)编写起来该是如何丑陋。*(根本问题)*
>
> 7. *错误代码不可以被忘掉(忽视)*。忘掉的后果见第3条。此外,有时候我们可能会故意不管某些错误,并用一个万能catch
> 来捕获所有未被捕获的错误,log,向支持网站发送错误报告,并重启程序。用异常这就很容易做到--只要写一个unhandled exception
> handler(不同语言对此的支持机制不一样)即可。*(假定存在工具可以检查)*
>
> 异常与错误代码的*本质区别之二*--*异常的传播使用的是一个单独的信道*


> ,而错误代码则占用了函数的返回值;函数的返回值本来的语意是用来返回"有用的"结果的,这个结果是属于程序的One True Path
> 的,而不是用来返回错误的。
>
> 利用返回值来传播错误导致的问题如下:
>

> 8. *所有函数都必须将返回值预留给错误*。如果你的函数最自然的语意是返回一个double,而每个double


> 都是有效的。不行,你得把这个返回值通道预留着给错误处理用。你可能会说,我的函数很简单,不会出错。但如果以后你修改了之后,函数复杂了呢?到那个时候再把返回的

> double改为int并加上一个double&作为out参数的话,改动可就大了。*(细节问题**/**人力问题)*
>
> 9. *返回值所能承载的错误信息是有限的*。NULL?-1?什么意思?具体的错误信息只能用GetLastError来提供...
> 哦,对了,你看见有多少人用过GetLastError的?*(人力问题)*
>
> 10. *不优雅的代码*。呃...
> 这个问题前面不是说过了么?不,这里说的是另一个不优雅之处--占用了用来返回结果的返回值通道。本来很自然的"计算--返回结果",变成了"计算--修改out
> 参数--返回错误"。当然,你可以说这个问题不是很严重。的确,将double res = readInput();改为double res;
> readInput(&res);也没什么大不了的。如果是连调用呢?比如,process(readInput());呃... 或者readInput() +
> ...?或者一般地,op1(op2(), op3(), ...);?*(细节问题**/**人力问题)*
>
> 11. *错误汇报方案不一致性*。看看Win32下面的错误汇报机制吧:HRESULT、BOOL、GetLastError...
> 本质上就是因为利用返回值通道是一个补丁方案,错误处理是程序的一个方面(aspect
> ),理应有其单独的汇报通道。利用异常的话,错误汇报方案就立即统一了,因为这是一个first-class的语言级支持机制。*(人力问题)*
>
> 12. *有些函数根本无法返回值*,如构造函数。*有些函数返回值是语言限制好了的*,如重载的操作符和隐式转换函数。*(根本问题**/**细节问题)*
>
> 异常与错误代码的*本质区别之三*--*异常本身能够携带任意丰富的信息*。
>
> 13. *有什么错误报告机制能比错误报告本身就包含尽量丰富的信息更好的呢?*


> 使用异常的话,你可以往异常类里面添加数据成员,添加成员函数,携带任意的信息(比如Java
> 的异常类就缺省携带了非常有用的调用栈信息)。而错误代码就只有一个单薄的数字或字符串,要携带其它信息只能另外存在其它地方,并期望你能通过

> GetLastError去查看。*(根本问题**/**人力问题)*
>
> 异常与错误代码的*本质区别之四*--*异常是**OO**的*。
>
> 14. *你可以设计自己的异常继承体系*。呃...那这又有什么用呢?当然有用,一个最大的好处就是你可以在任意抽象层次上catch一组异常(exception
> grouping),比如你可以用catch(IOException)来捕获所有的IO异常,用catch(SQLException)来捕获所有的SQL
> 异常。用catch(FileException)来catch所有的文件异常。你也可以catch更明确一点的异常,如StreamEndException
> 。总之,catch的粒度是粗是细,根据需要,随你调节。当然了,你可以设计自己的新异常。能够catch
> 一组相关异常的好处就是你可以很方便的对他们做统一的处理。*(根本问题)*
>
> 异常与错误代码的*本质区别之五*--*异常是强类型的*。
>
> 15. *异常是强类型的*。在catch异常的时候,一个特定类型的catch只能catch类型匹配的异常。而用error-code的话,就跟enum
> 一样,类型不安全。-1 == foo()?FAILED == foo()?MB_OK == foo()?大家反正都是整数。*(细节问题)*
>
> 异常与错误代码的*本质区别之六*--*异常是**first-class**的语言机制*。
>
> 16. *代码分析工具可以识别出异常并进行各种监测或分析*。比如PerfMon就会对程序中的异常做统计。这个好处放在未来时态下或许怎么都不应该小觑。*
> (未知问题)*
>
> * *


>
> 综上,真正根本的问题有:
>
> 6. 没有异常,根本无法编写泛型组件。
>
> 14. 你可以设计自己的异常继承体系。
>
> 16. 代码分析工具可以识别出异常并进行各种监测或分析。
>
> 对于第6点,如果你的项目中不需要编写泛型组件,那么这一点约束便不存在。
>
> 第14点,似乎在实际项目中也没那么关键。其实也可以归结为人力问题。
>
> 第16点的优势目前还不明确。
>
> 总结:
>
> 因此,看上去,异常的缺点是,需要学习掌握一门新的机制(而且并不容易),优点是能够使错误处理代码变得很优雅,能够省掉许多人力。而错误处理代码的优点是每个人都会,只需要强制执行编码规范和使用工具(可以假定存在这样的工具,因为如果不存在的话,异常的
> N多优点就又都浮现出来了),也可以确保错误安全。缺点正对应于异常的优点。
>
> 结论似乎很明显:使用异常,由于没有合格的开发人员,所以异常的优点甚至没有发挥的机会,结论就是事与愿违。而使用错误代码,每个人都知道怎么做,只要加以严格规范,和使用工具(如果有的话),虽然代码丑陋,但还是能够做到错误安全的。
>
> 现实问题:
>
> 1. 存在检查是否check了每个可能的错误返回值的工具吗?
>
> 2. 如果存在,你的项目用了吗?进一步,有多少现实的项目用了呢?(我知道在Joel的项目中是通过编码规范来约束开发人员"一定要check
> 所有返回值"的)。
>
> 3. 通过编码规范来约束,可靠吗?(或许总比依赖于程序员对异常这一语言特性的掌握程度可靠?)
>
> 4. 如果没有这样的工具,在未来可能出现这样的工具吗?如果存在,可能普及吗?(我的认为是不可能,因为异常在目前的所有主流语言中都是唯一的正规军)。
>
> 5. 掌握异常这一语言真的很困难吗?"没有合格的开发人员"的问题的根源是什么?是因为这一语言特性真的很困难?还是其它什么原因。
>
> 最后:

> ...
>
> read more

莫华枫

unread,
Oct 29, 2007, 2:59:04 AM10/29/07
to pon...@googlegroups.com
我们公司通过了cmm3级,制定了严格的过程。所有过程中,什么都能够想办法做到,唯独peer review一项,根本无法坚持下去。因为成本太高了。
review一次,需要至少看一遍代码,同时还需要编码的人介绍一些相关的内容。这样至少要准备近一个星期。而且,即便这样,review的人也无法完全地找出问题。我们几个老鸟也经常让其他人的代码"带进沟里"。这就要求不只一个人,背靠背地review代码。这样,两三个人,个把星期的时间,而且还不止一次。这种成本对于多数企业是无法承受的。
所以,仅尝试了一两次,peer review就变成了"过一遍"。没有产生任何实际效果。
review对于需求最有用,其次是设计,但代码review是得不偿失的。相比之下,把这些资源用于测试,会得到更好的效果。而且测试解决了包括错误处理在内的很多问题。我们不可能因为做了review而削弱测试,这种风险是无法承担的。
但在测试中,异常比错误码更加"显眼",更容易发现问题。


在07-10-29,pongba <pon...@gmail.com> 写道:

pongba

unread,
Oct 29, 2007, 3:24:27 AM10/29/07
to pon...@googlegroups.com
On 10/29/07, 莫华枫 <longsh...@gmail.com> wrote:
我们公司通过了cmm3级,制定了严格的过程。所有过程中,什么都能够想办法做到,唯独peer review一项,根本无法坚持下去。因为成本太高了。
review一次,需要至少看一遍代码,同时还需要编码的人介绍一些相关的内容。这样至少要准备近一个星期。而且,即便这样,review的人也无法完全地找出问题。我们几个老鸟也经常让其他人的代码"带进沟里"。这就要求不只一个人,背靠背地review代码。这样,两三个人,个把星期的时间,而且还不止一次。这种成本对于多数企业是无法承受的。
所以,仅尝试了一两次,peer review就变成了"过一遍"。没有产生任何实际效果。
review对于需求最有用,其次是设计,但代码review是得不偿失的。相比之下,把这些资源用于测试,会得到更好的效果。而且测试解决了包括错误处理在内的很多问题。我们不可能因为做了review而削弱测试,这种风险是无法承担的。

cber却说review烧钱没这么多,而且效果也比这要好。矛盾出现。
cber? :P 

但在测试中,异常比错误码更加"显眼",更容易发现问题。

这句话听起来很有力啊:)

莫华枫

unread,
Oct 29, 2007, 3:36:24 AM10/29/07
to pon...@googlegroups.com

但在测试中,异常比错误码更加"显眼",更容易发现问题。

这句话听起来很有力啊:)

说句题外话。:P
有时,事情总是充满矛盾。虽说这是好事,但是,我们的一些程序员却由此养成了一个坏习惯。反正异常总会被捕捉到,那也没必要费心去处理这些异常。于是,除了那些需要进行业务处理的异常,其他的都一律不捕捉。结果就是时不时地跳出个系统级异常对话框,客户看着一头雾水。电话打过来,我们态度诚恳地告诉他选错了文件,客户也就做罢了。
有时我还真想给我们的客户写封感谢信,对他们的这种宽容和忍耐加以褒奖。(或许应该送面锦旗?)
唉,为什么好东西总是被人乱用啊。:(

pongba

unread,
Oct 29, 2007, 3:38:30 AM10/29/07
to pon...@googlegroups.com
On 10/29/07, 莫华枫 <longsh...@gmail.com> wrote:

但在测试中,异常比错误码更加"显眼",更容易发现问题。

这句话听起来很有力啊:)

说句题外话。:P
有时,事情总是充满矛盾。虽说这是好事,但是,我们的一些程序员却由此养成了一个坏习惯。反正异常总会被捕捉到,那也没必要费心去处理这些异常。于是,除了那些需要进行业务处理的异常,其他的都一律不捕捉。结果就是时不时地跳出个系统级异常对话框,客户看着一头雾水。电话打过来,我们态度诚恳地告诉他选错了文件,客户也就做罢了。
有时我还真想给我们的客户写封感谢信,对他们的这种宽容和忍耐加以褒奖。(或许应该送面锦旗?)
唉,为什么好东西总是被人乱用啊。:(

其实要解决这个问题只要加一条编码准则:在任何功能模块的出口加上fault barrier(也就是某种general catch)。

莫华枫

unread,
Oct 29, 2007, 3:43:16 AM10/29/07
to pon...@googlegroups.com
嗯,好办法,记下了。回头去教训他们。:)

在07-10-29,pongba <pon...@gmail.com> 写道:

wang xin

unread,
Oct 29, 2007, 4:20:18 AM10/29/07
to pon...@googlegroups.com


在07-10-29,pongba <pon...@gmail.com> 写道:

On 10/29/07, 莫华枫 <longsh...@gmail.com > wrote:
我们公司通过了cmm3级,制定了严格的过程。所有过程中,什么都能够想办法做到,唯独peer review一项,根本无法坚持下去。因为成本太高了。
review一次,需要至少看一遍代码,同时还需要编码的人介绍一些相关的内容。这样至少要准备近一个星期。而且,即便这样,review的人也无法完全地找出问题。我们几个老鸟也经常让其他人的代码"带进沟里"。这就要求不只一个人,背靠背地review代码。这样,两三个人,个把星期的时间,而且还不止一次。这种成本对于多数企业是无法承受的。
所以,仅尝试了一两次,peer review就变成了"过一遍"。没有产生任何实际效果。
review对于需求最有用,其次是设计,但代码review是得不偿失的。相比之下,把这些资源用于测试,会得到更好的效果。而且测试解决了包括错误处理在内的很多问题。我们不可能因为做了review而削弱测试,这种风险是无法承担的。

cber却说review烧钱没这么多,而且效果也比这要好。矛盾出现。
cber? :P 

不同的公司流程不一样,不一定CMM3就代表他们的流程要好。根据我的经验,除了在一群菜鸟中进行review时间会长一些之外(大概需要3天,因为这时你得给他们讲解你的代码;当然,这样也有好处,可以强迫你自己反思一下自己的实现),没有一次的review会超过1天(我们通常都是把设计文档和改动的代码一起发给reviewer看)

但在测试中,异常比错误码更加"显眼",更容易发现问题。

这句话听起来很有力啊:)

"显眼 ":| 大部分的"显眼"的问题都不是问题,不显眼的才是

莫华枫

unread,
Oct 29, 2007, 4:24:58 AM10/29/07
to pon...@googlegroups.com


在07-10-29,wang xin <cber.w...@gmail.com> 写道:


在07-10-29,pongba <pon...@gmail.com > 写道:

On 10/29/07, 莫华枫 < longsh...@gmail.com > wrote:
我们公司通过了cmm3级,制定了严格的过程。所有过程中,什么都能够想办法做到,唯独peer review一项,根本无法坚持下去。因为成本太高了。
review一次,需要至少看一遍代码,同时还需要编码的人介绍一些相关的内容。这样至少要准备近一个星期。而且,即便这样,review的人也无法完全地找出问题。我们几个老鸟也经常让其他人的代码"带进沟里"。这就要求不只一个人,背靠背地review代码。这样,两三个人,个把星期的时间,而且还不止一次。这种成本对于多数企业是无法承受的。
所以,仅尝试了一两次,peer review就变成了"过一遍"。没有产生任何实际效果。
review对于需求最有用,其次是设计,但代码review是得不偿失的。相比之下,把这些资源用于测试,会得到更好的效果。而且测试解决了包括错误处理在内的很多问题。我们不可能因为做了review而削弱测试,这种风险是无法承担的。

cber却说review烧钱没这么多,而且效果也比这要好。矛盾出现。
cber? :P 

不同的公司流程不一样,不一定CMM3就代表他们的流程要好。根据我的经验,除了在一群菜鸟中进行review时间会长一些之外(大概需要3天,因为这时你得给他们讲解你的代码;当然,这样也有好处,可以强迫你自己反思一下自己的实现),没有一次的review会超过1天(我们通常都是把设计文档和改动的代码一起发给reviewer看)

peer review是cmm的要求,没办法。你是说review自己的代码?那倒是会简单很多。只是这效果...。我还是主张更好的测试。

但在测试中,异常比错误码更加"显眼",更容易发现问题。

这句话听起来很有力啊:)

"显眼 ":| 大部分的"显眼"的问题都不是问题,不显眼的才是

异常就是把不显眼的东西搞得显眼了。

--
Everything is possible and available if we trust ourselves!

pongba

unread,
Oct 29, 2007, 4:26:13 AM10/29/07
to pon...@googlegroups.com
On 10/29/07, wang xin <cber.w...@gmail.com> wrote:
我相信longshanks的意思是异常有助于将一些不显眼的问题转化为显眼的问题。如果程序在一个错误点上没有抛出异常(因为用的是错误代码返回),也没有被check,并且百密一疏的review过程也没有检查出来,那么程序就会带着错误状态往下执行,结果就是一个明显的问题变成一个不明显的问题,执行的结果也许是崩溃,也许是看上去一切正常。如果是异常的话,就不存在这个危险,程序会立即停止往下执行,如果没有catch,就会明显地失败。

当然,这个逻辑也有一个关键的漏洞:1. 如果review不存在百密一疏的话这个逻辑就不存在。2. 如果这类错误可以由其它路径排查(测试?),或者这类错误少到根本无伤大雅的程度的话,这逻辑也不成立。

wang xin

unread,
Oct 29, 2007, 4:27:14 AM10/29/07
to pon...@googlegroups.com


在07-10-29,莫华枫 <longsh...@gmail.com> 写道:


在07-10-29,wang xin <cber.w...@gmail.com > 写道:


在07-10-29,pongba <pon...@gmail.com > 写道:

On 10/29/07, 莫华枫 < longsh...@gmail.com > wrote:
我们公司通过了cmm3级,制定了严格的过程。所有过程中,什么都能够想办法做到,唯独peer review一项,根本无法坚持下去。因为成本太高了。
review一次,需要至少看一遍代码,同时还需要编码的人介绍一些相关的内容。这样至少要准备近一个星期。而且,即便这样,review的人也无法完全地找出问题。我们几个老鸟也经常让其他人的代码"带进沟里"。这就要求不只一个人,背靠背地review代码。这样,两三个人,个把星期的时间,而且还不止一次。这种成本对于多数企业是无法承受的。
所以,仅尝试了一两次,peer review就变成了"过一遍"。没有产生任何实际效果。
review对于需求最有用,其次是设计,但代码review是得不偿失的。相比之下,把这些资源用于测试,会得到更好的效果。而且测试解决了包括错误处理在内的很多问题。我们不可能因为做了review而削弱测试,这种风险是无法承担的。

cber却说review烧钱没这么多,而且效果也比这要好。矛盾出现。
cber? :P 

不同的公司流程不一样,不一定CMM3就代表他们的流程要好。根据我的经验,除了在一群菜鸟中进行review时间会长一些之外(大概需要3天,因为这时你得给他们讲解你的代码;当然,这样也有好处,可以强迫你自己反思一下自己的实现),没有一次的review会超过1天(我们通常都是把设计文档和改动的代码一起发给reviewer看)

peer review是cmm的要求,没办法。你是说review自己的代码?那倒是会简单很多。只是这效果...。我还是主张更好的测试。

我们所进行的review也是peer review,只是现在在国内因为刚刚毕业的小朋友比较多,有时需要带领他们熟悉一下review的流程,所以会多了一个讲解的过程:$

但在测试中,异常比错误码更加"显眼",更容易发现问题。

这句话听起来很有力啊:)

"显眼 ":| 大部分的"显眼"的问题都不是问题,不显眼的才是

异常就是把不显眼的东西搞得显眼了。

能够被发现并被用来封装成异常的东西,还不够显眼:O

--
Everything is possible and available if we trust ourselves!




--
反者道之动,弱者道之用
m...@seaskysh.com
longsh...@gmail.com
http://blog.csdn.net/longshanks/

wang xin

unread,
Oct 29, 2007, 4:29:56 AM10/29/07
to pon...@googlegroups.com


在07-10-29,pongba <pon...@gmail.com> 写道:

当然,这个逻辑也有一个关键的漏洞:1. 如果review不存在百密一疏的话这个逻辑就不存在。2. 如果这类错误可以由其它路径排查(测试?),或者这类错误少到根本无伤大雅的程度的话,这逻辑也不成立。

再次重申一下,review是最有效地找到代码问题的(终极?)手段 (这个论证是我以前看某调查文章里面提到的),如果review都发现不了的问题,基本上在实现人员的开发过程中都不会被发现也很难于被封装成异常抛出

莫华枫

unread,
Oct 29, 2007, 4:41:26 AM10/29/07
to pon...@googlegroups.com
能不能发现错误,对于是否使用异常都是一样的。一个状态错误在使用异常的时候无法发现,难道使用error code就能发现吗?

Googol Lee

unread,
Oct 29, 2007, 4:44:33 AM10/29/07
to TopLanguage
关于这个,我觉得是有些错误实在无法区分到底应该抛异常,还是应该属于正常的流程。按理说,用户选错文件是很正常的一件事情(马有失手人有错蹄
嘛......),但因为这个就抛异常,却不是异常的本意了。

On Oct 29, 3:36 pm, "莫华枫" <longshank...@gmail.com> wrote:
> > 但在测试中,异常比错误码更加"显眼",更容易发现问题。
>
> > 这句话听起来很有力啊:)
>
> 说句题外话。:P
> 有时,事情总是充满矛盾。虽说这是好事,但是,我们的一些程序员却由此养成了一个坏习惯。反正异常总会被捕捉到,那也没必要费心去处理这些异常。于是,除了那些需要进行业务处理的异常,其他的都一律不捕捉。结果就是时不时地跳出个系统级异常对话框,客户看着一头雾水。电话打过来,我们态度诚恳地告诉他选错了文件,客户也就做罢了。
> 有时我还真想给我们的客户写封感谢信,对他们的这种宽容和忍耐加以褒奖。(或许应该送面锦旗?)
> 唉,为什么好东西总是被人乱用啊。:(
>
> --
> 反者道之动,弱者道之用
> m...@seaskysh.com

> longshank...@gmail.comhttp://blog.csdn.net/longshanks/

莫华枫

unread,
Oct 29, 2007, 4:46:20 AM10/29/07
to pon...@googlegroups.com
我的体会是,检测错误最好的手段是编码时防止错误和测试。前者没什么好说的,方法一大堆,效果不见得很好。后者却是客观的。相比review,可以更方便准确地找到错误。毕竟review是个主观的过程,甚至可能受reviewer心情的影响。:)


在07-10-29,wang xin < cber.w...@gmail.com> 写道:

莫华枫

unread,
Oct 29, 2007, 4:48:48 AM10/29/07
to pon...@googlegroups.com
是这样的,选错文件是库抛出来的错误,但是系统级的信息可读性差。应当捕捉后显示更人性化的信息。可很多程序员偷懒,或者疏忽,这就不应该了。


在07-10-29,Googol Lee <goog...@gmail.com > 写道:

pongba

unread,
Oct 29, 2007, 4:50:32 AM10/29/07
to pon...@googlegroups.com
On 10/29/07, wang xin <cber.w...@gmail.com> wrote:


在07-10-29,pongba <pon...@gmail.com > 写道:

当然,这个逻辑也有一个关键的漏洞:1. 如果review不存在百密一疏的话这个逻辑就不存在。2. 如果这类错误可以由其它路径排查(测试?),或者这类错误少到根本无伤大雅的程度的话,这逻辑也不成立。

再次重申一下,review是最有效地找到代码问题的(终极?)手段 (这个论证是我以前看某调查文章里面提到的),如果review都发现不了的问题,基本上在实现人员的开发过程中都不会被发现也很难于被封装成异常抛出

我记得这里讨论的范畴应该是在错误传播过程中的代码;而非错误发生点上的代码。
可以假设,所有真正的错误,在发生点上都能够被抛出(不管是异常还是错误代码)。
而在错误传播过程中,我的意思是,使用错误代码的话,因为要层层手动传播,靠严格的review过程,也是不足够的,百密一疏的情况总会发生,而一旦发生,程序必然会带着错误状态往下执行,轻则被动崩溃,重则毫无明显症状;我推测为什么大量软件会莫名其妙地被动崩溃("非法操作"),就是因为错误传播链里面除了疏忽,一环疏忽,则全盘皆输。另一方面,再来看异常,异常的话,中间的传播链条是自动的,开发者只要管好stack-unwinding过程中的事情就好了。所以就避免了因手动传播失误而导致的被动崩溃(被动崩溃是危险的,因为程序不再可控,什么情况都可能发生)。我推测Eclipse在错误发生情况下的良好表现就是因为异常机制的好处,Eclipse用的时候也经常发生错误,但我的经验是都能体面inform用户和退出模块。如果用错误,可能还没等到错误代码传播到fault-barrier,就因为某个环节失误而不雅地崩溃了。

--
Everything is possible and available if we trust ourselves!

wang xin

unread,
Oct 29, 2007, 4:51:14 AM10/29/07
to pon...@googlegroups.com
请注意,我原文说的是"review是最有效地找到代码问题的(终极?)手段",指的是"问题"而不是"错误"、所有的代码在review前基本上肯定是被N轮测试过了;review的作用就是把一些测试也发现不了的潜在的问题给抓出来

在07-10-29, 莫华枫 <longsh...@gmail.com> 写道:

李扬

unread,
Oct 29, 2007, 4:54:31 AM10/29/07
to pon...@googlegroups.com
八封下:))))))
 
 
看来pongba 应该再开个贴《关于异常的最最最终讨论》!
 
 
呵呵开个玩笑。大家别介意哈。调节下气氛。

 
在07-10-29,莫华枫 <longsh...@gmail.com> 写道:

wang xin

unread,
Oct 29, 2007, 4:54:56 AM10/29/07
to pon...@googlegroups.com


在07-10-29,pongba <pon...@gmail.com> 写道:


On 10/29/07, wang xin < cber.w...@gmail.com> wrote:


在07-10-29,pongba <pon...@gmail.com > 写道:

当然,这个逻辑也有一个关键的漏洞:1. 如果review不存在百密一疏的话这个逻辑就不存在。2. 如果这类错误可以由其它路径排查(测试?),或者这类错误少到根本无伤大雅的程度的话,这逻辑也不成立。

再次重申一下,review是最有效地找到代码问题的(终极?)手段 (这个论证是我以前看某调查文章里面提到的),如果review都发现不了的问题,基本上在实现人员的开发过程中都不会被发现也很难于被封装成异常抛出

我记得这里讨论的范畴应该是在错误传播过程中的代码;而非错误发生点上的代码。
可以假设,所有真正的错误,在发生点上都能够被抛出(不管是异常还是错误代码)。
而在错误传播过程中,我的意思是,使用错误代码的话,因为要层层手动传播,靠严格的review过程,也是不足够的,百密一疏的情况总会发生,而一旦发生,程序必然会带着错误状态往下执行,轻则被动崩溃,重则毫无明显症状;我推测为什么大量软件会莫名其妙地被动崩溃("非法操作"),就是因为错误传播链里面除了疏忽,一环疏忽,则全盘皆输。另一方面,再来看异常,异常的话,中间的传播链条是自动的,开发者只要管好stack-unwinding过程中的事情就好了。所以就避免了因手动传播失误而导致的被动崩溃(被动崩溃是危险的,因为程序不再可控,什么情况都可能发生)。我推测Eclipse在错误发生情况下的良好表现就是因为异常机制的好处,Eclipse用的时候也经常发生错误,但我的经验是都能体面inform用户和退出模块。如果用错误,可能还没等到错误代码传播到fault-barrier,就因为某个环节失误而不雅地崩溃了。

难道是我走题了:$
错误在发生时就被抓住,难道不比被异常散播出去跟好吗?(开始胡言乱语了……)

--
Everything is possible and available if we trust ourselves!




--
刘未鹏(pongba)|C++的罗浮宫
http://blog.csdn.net/pongba
TopLanguage
http://groups.google.com/group/pongba

莫华枫

unread,
Oct 29, 2007, 5:05:27 AM10/29/07
to pon...@googlegroups.com
错误发生了,我们会有几种选择?
一、就地正法,立刻处理掉。
二、上诉到更高级的法院。异常直接抛出去,哪级法院管得了的,就截住,按律问典。如果谁也管不了,那就最高法院接着。案子总能处理的。error code则逐级上诉,直到最高法院。但是有可能哪级法院忘了审了,这就...。
呵呵,这就是两者的差别。:P

在07-10-29, wang xin <cber.w...@gmail.com> 写道:

red...@gmail.com

unread,
Oct 29, 2007, 5:26:51 AM10/29/07
to pon...@googlegroups.com

> 我推测为什么大量软件会莫名其妙地被动崩溃("非法操作"),就是因为错误传
> 播链里面除了疏忽,一环疏忽,则全盘皆输。
我自己的经历是, 如果程序员还说得过去, 大多数错误主要还是在发生地没有被发
现(多数设计或者编码错误, 修改之后未必需要引入一个runtime 错误), 而不是在
错误传播链中的问题.

Atry

unread,
Oct 29, 2007, 5:35:03 AM10/29/07
to pon...@googlegroups.com
嗯,这么说的话,用什么方式传播错误其实不重要?

在07-10-29,red...@gmail.com <red...@gmail.com> 写道:

pongba

unread,
Oct 29, 2007, 5:47:32 AM10/29/07
to pon...@googlegroups.com


On 10/29/07, Atry <pop....@gmail.com> wrote:
嗯,这么说的话,用什么方式传播错误其实不重要?

我认为是重点看哪种传播方式更保险,这里保险的意思是,能否安全传播到受理点。
那么,是编译器保证的自动传播更保险,还是手动传播?
如果排开保险问题不谈,用什么方式传播错误,对"错误如何处理"这个问题的本质没有关系。(但跟软件的健壮性大有关系)。

pongba

unread,
Oct 29, 2007, 5:56:52 AM10/29/07
to pon...@googlegroups.com
意思就是大部分错误都是编程错误?好吧,我想作为一个程序员,我想大家都有体会的是,大多数错误(那些需要debug的),当然都是编程错误。

然而,关键是,"异常OR错误代码"的这个争论,本来针对的问题领域就是"剩下来的小部分错误",即那些unexpected condition。这些condition也许连1%都不到,但却关乎软件在极端条件下的健壮性。比如内存耗尽这种问题,真是千载难逢,那么是不是意味着就不用考虑了呢?(的确也有许多时候不用考虑,还是看软件的应用领域)。

简而言之,如果一个condition使得程序不能完成既定任务,就是异常。这类情况虽然不多见,但一旦出现,就是考验软件的时候了,能否体面退出,就关键了,不是么?

那么如何体面退出呢?最牛逼的,如果当即abort(),就连错误代码都用不着了。云风说他们的项目就主要是这么做的,因为他们的项目有智能GC,无论何时abort()程序,都能保证清理资源。而且由于项目特点,任何退出点都能保护关键数据。这就使得他们的项目中很少有错误需要层层上报的。但我觉得现实中像这样的项目应该是少数(猜测)。那么就有两个问题:一,是否现实中的项目都应该采取云风的那种方案?(听起来很迷人,不是么?)二,如果不能采取那种方案,而需要上报到一个边界,然后再退出的话,那么如何确保上报的过程中不出叉子?(cber的意思是,review。可是我觉得review还不够保险,错误上报有其特殊性——一旦某一个环节出问题,则全盘皆输。另一方面,有什么保证机制比编译器自动保证(异常自动传播)更令人放心?不仅放心,而且省事。)

red...@gmail.com

unread,
Oct 29, 2007, 5:57:55 AM10/29/07
to pon...@googlegroups.com
我碰到的情况, 错误传播方式, 似乎这不是一个特别严重的问题, 可能的原因是, 已经发现, 而没有被正确传播的错误, 在难抓的错误中, 是属于好抓的一种的,

1. 相对有些线索
2. 碰到让人挠破头的bug的时候, 某个函数的返回值没有被正常处理这种情况也会让破头的人查到的.

但是, 确实存在, 只修复了一个层次, 而不是整个 caller 链被检查处理, 下次又要再debug 一次的情况(我是记得有发生的).

要避免这种情况, 恐怕是需要强制所有的函数都是丑陋的返回值方式, 例如 COM 的那种, void 函数都不要了, 代价也很高.

异常在这种情况下, 就好多了.

Atry 写道:

red...@gmail.com

unread,
Oct 29, 2007, 6:17:45 AM10/29/07
to pon...@googlegroups.com
pongba 写道:


On 10/29/07, red...@gmail.com <red...@gmail.com> wrote:

> 我推测为什么大量软件会莫名其妙地被动崩溃("非法操作"),就是因为错误传
> 播链里面除了疏忽,一环疏忽,则全盘皆输。
我自己的经历是, 如果程序员还说得过去, 大多数错误主要还是在发生地没有被发
现(多数设计或者编码错误, 修改之后未必需要引入一个runtime 错误), 而不是在
错误传播链中的问题.
意思就是大部分错误都是编程错误?好吧,我想作为一个程序员,我想大家都有体会的是,大多数错误(那些需要debug的),当然都是编程错误。

然而,关键是,"异常OR错误代码"的这个争论,本来针对的问题领域就是"剩下来的小部分错误",即那些unexpected condition。这些condition也许连1%都不到,但却关乎软件在极端条件下的健壮性。比如内存耗尽这种问题,真是千载难逢,那么是不是意味 着就不用考虑了呢?(的确也有许多时候不用考虑,还是看软件的应用领域)。
  题外话:
  由于 Windows 下愚蠢的软件不少, 我在 Windows 下倒有很多次耗尽内存的经验了, 可以看出当今的 Windows 程序, 也没有几个有好的适应能力的, 千奇百怪的错误都有, 有些工具栏没有图标, 其他功能勉强可用; 有的想弹出一个窗口通知我, 上面确是什么都显示不出来 (这两种, 或许责任在 windows), 崩溃的也不少, 有些也不崩溃, 开始耗很多 CPU ...... 似乎弹出窗口告诉我, 内存耗尽, 文件已经保存, 现在退出的程序没几个 --- 可能就是 windows 在愚蠢, 还在 "临时增大 pagefile", 程序以为内存还有.

  Linux 下面简单, 内存不够了, 选一个进程, 杀死, 释放内存出来 ---- 如果杀的正好是最重要的, 哈哈.

pongba

unread,
Oct 29, 2007, 6:26:51 AM10/29/07
to pon...@googlegroups.com
我也扯句题外话,哈哈:-)
云风曾经提倡过使用多进程模型,为什么?因为多进程更健壮,可以完全隔离错误。一个进程随便怎么把工作给screw up也没关系。就算"非法操作"也没关系。
相反,多线程就做不到了。搞一个解引用空指针,访问一个非法内存,整个进程都跟着遭殃。不管那个非法动作处于哪个模块。Java和.NET平台因而封装了NullReferenceException(具体名字忘了),这就是有VM的好处啊,唉。。。
此外,实际编程中不可能把每一个功能模块都用一个进程来表达吧?所以还是得考虑超轻量级模块(一个函数?一个线程)的错误处理问题。如何在内部出了不知名的错误的时候杀掉这个模块,而不破坏程序的整体状态呢?Java/.NET连解空引用都能用异常抛出,这就很好办了,只要在模块边界catch-all,然后就可以退出了,资源会泄漏吗?只要确保error-safe,资源也不会泄漏(除非清理资源的逻辑也出了问题,那就问题大了)。所以我觉得这个才是错误处理的最佳办法。

哦,对了,谁说的来着,现实当中,真正可以恢复的错误只占10%不到。真正可以恢复的错误往往根本不往上级传播,而是就地解决?往上传播的错误最终导致的几乎总是cancel某个模块(?)

red...@gmail.com

unread,
Oct 29, 2007, 6:38:23 AM10/29/07
to pon...@googlegroups.com
pongba 写道:

> 我也扯句题外话,哈哈:-)
> 云风曾经提倡过使用多进程模型,为什么?因为多进程更健壮,可以完全隔离错
> 误。一个进程随便怎么把工作给screw up也没关系。就算"非法操作"也没关系。
那得要够松耦合, 如果考虑到了性能, 要用到共享内存, 那情形还是好得不多, 共
享内存可能被弄坏.

> 相反,多线程就做不到了。搞一个解引用空指针,访问一个非法内存,整个进程
> 都跟着遭殃。不管那个非法动作处于哪个模块。 Java和.NET平台因而封装了
> NullReferenceException(具体名字忘了),这就是有VM的好处啊,唉。。。
这个, 当今的 OS 都可以捕获 null 指针访问的. 除非你用了没有内存管理单元的
CPU, 例如 arm7 之类.

Windows 下会引发 SEH, SEH 可以转换成 C++ 异常,但是类似内存错误访问的异
常, 更多时候应该是 print stack, 然后退出才好, 否则天知道后面还有什么事情 ?

posix 下面是引发一个 signal, signal 似乎不怎么容易转换成 C++ 异常, 但是
signal 的栈一样可以打印.

所以, 没有 VM 似乎也没有什么太了不起啊.
> 此外,实际编程中不可能把每一个功能模块都用一个进程来表达吧?所以还是得
> 考虑超轻量级模块(一个函数?一个线程)的错误处理问题。如何在内部出了不
> 知名的错误的时候杀掉这个模块,而不破坏程序的整体状态呢?Java/.NET连解
> 空引用都能用异常抛出,这就很好办了,只要在模块边界catch-all,然后就可
> 以退出了,资源会泄漏吗?只要确保error-safe,资源也不会泄漏(除非清理资
> 源的逻辑也出了问题,那就问题大了)。所以我觉得这个才是错误处理的最佳办
> 法。


>
> 哦,对了,谁说的来着,现实当中,真正可以恢复的错误只占10%不到。真正可
> 以恢复的错误往往根本不往上级传播,而是就地解决?往上传播的错误最终导致
> 的几乎总是cancel某个模块(?)

编码错误先不考虑在内:

多数程序, 初始化/re config 时候就会将访问的文件访问了, 要bind 的port
bind 了, 等等, 所以, 能够正常跑起来之后, 再碰到严重错误的情形应该不多,
但是一旦碰到, 可能就是类似磁盘耗尽, 内存耗尽这种极端了, 此时, 除了记录错
误退出, 没有太多事情好做 ----- 或者帮用户杀掉几个进程, 删除几个文件来恢
复一下 ;)


pongba

unread,
Oct 29, 2007, 6:44:43 AM10/29/07
to pon...@googlegroups.com
On 10/29/07, red...@gmail.com <red...@gmail.com> wrote:
pongba 写道:
> 我也扯句题外话,哈哈:-)
> 云风曾经提倡过使用多进程模型,为什么?因为多进程更健壮,可以完全隔离错
> 误。一个进程随便怎么把工作给screw up也没关系。就算"非法操作"也没关系。
那得要够松耦合, 如果考虑到了性能, 要用到共享内存, 那情形还是好得不多, 共
享内存可能被弄坏.

> 相反,多线程就做不到了。搞一个解引用空指针,访问一个非法内存,整个进程
> 都跟着遭殃。不管那个非法动作处于哪个模块。 Java和.NET平台因而封装了
> NullReferenceException(具体名字忘了),这就是有VM的好处啊,唉。。。
这个, 当今的 OS 都可以捕获 null 指针访问的. 除非你用了没有内存管理单元的
CPU, 例如 arm7 之类.

Windows 下会引发 SEH, SEH 可以转换成 C++ 异常,但是类似内存错误访问的异
常, 更多时候应该是 print stack, 然后退出才好, 否则天知道后面还有什么事情 ?

posix 下面是引发一个 signal, signal 似乎不怎么容易转换成 C++ 异常, 但是
signal 的栈一样可以打印.

所以, 没有 VM 似乎也没有什么太了不起啊.

这个... 我还是那个观点,不是可行性的问题, 而是便利程度的问题。VM提供了first-class的NullRefException异常,不仅方便,而且利于编写可移植的代码。
Windows是可以用SEH来catch,Linux是可以用signal机制,但有几个人care to use的...?

> 此外,实际编程中不可能把每一个功能模块都用一个进程来表达吧?所以还是得
> 考虑超轻量级模块(一个函数?一个线程)的错误处理问题。如何在内部出了不
> 知名的错误的时候杀掉这个模块,而不破坏程序的整体状态呢?Java/.NET连解
> 空引用都能用异常抛出,这就很好办了,只要在模块边界catch-all,然后就可
> 以退出了,资源会泄漏吗?只要确保error-safe,资源也不会泄漏(除非清理资
> 源的逻辑也出了问题,那就问题大了)。所以我觉得这个才是错误处理的最佳办
> 法。
>
> 哦,对了,谁说的来着,现实当中,真正可以恢复的错误只占10%不到。真正可
> 以恢复的错误往往根本不往上级传播,而是就地解决?往上传播的错误最终导致
> 的几乎总是cancel某个模块(?)
编码错误先不考虑在内:

多数程序, 初始化/re config 时候就会将访问的文件访问了, 要bind 的port
bind 了, 等等, 所以, 能够正常跑起来之后, 再碰到严重错误的情形应该不多,
但是一旦碰到, 可能就是类似磁盘耗尽, 内存耗尽这种极端了, 此时, 除了记录错
误退出, 没有太多事情好做 ----- 或者帮用户杀掉几个进程, 删除几个文件来恢
复一下 ;)
Yup,正是如此!
但也有比如传输出错、网线被人踩了啥的... 总之错误处理处理的都是些极端情况;而大部分应用根本不关心。所以cber说得没错,作为大众程序员,我们的确是正在竭尽全力务虚中 :P

pongba

unread,
Oct 29, 2007, 6:47:05 AM10/29/07
to pon...@googlegroups.com
对了,网络程序的运行过程中出现的异常应该是最多的吧?相对于本地资源,网络资源太不可靠了。

有谁研究过ppLive的?ppLive是用的多进程模型。我用来看网络电视的时候老是有进程啪嗒一声崩溃掉(是"非法访问"式崩溃),但主界面程序还好好的...

CAXSOFT

unread,
Oct 29, 2007, 8:43:03 AM10/29/07
to pon...@googlegroups.com
理想状态啊
得考虑下实际情况吧

 
在07-10-29,pongba <pon...@gmail.com> 写道:
OpenCASCADE+VTK+wxWidgets+C++

yq chen

unread,
Oct 29, 2007, 9:25:17 AM10/29/07
to pon...@googlegroups.com
COM的C++变成模型是使用返回值的典型代表,难道真的很漂亮?难道真的每个返回的HRESULT值都能够被仔细的分析处理过?
是人总是会出错的,不管你采用多少手段。

在07-10-29,red...@gmail.com < red...@gmail.com> 写道:

freefalcon

unread,
Oct 29, 2007, 10:59:12 AM10/29/07
to TopLanguage
>
> 9. *返回值所能承载的错误信息是有限的*。NULL?-1?什么意思?具体的错误信息只能用GetLastError来提供...
> 哦,对了,你看见有多少人用过GetLastError的?*(人力问题)*

GetLastError我倒是经常用的(主要还是根据API的说明看有没有必要用),不过返回值承载的信息确实有限,异常发生时,我们最想知道的是错
误的文字描述信息和函数调用栈,这用异常将会很容易实现

>
> 10. *不优雅的代码*。呃...
> 这个问题前面不是说过了么?不,这里说的是另一个不优雅之处--占用了用来返回结果的返回值通道。本来很自然的"计算--返回结果",变成了"计算--修改ou t
> 参数--返回错误"。当然,你可以说这个问题不是很严重。的确,将double res = readInput();改为double res;
> readInput(&res);也没什么大不了的。如果是连调用呢?比如,process(readInput());呃... 或者readInput() +
> ...?或者一般地,op1(op2(), op3(), ...);?*(细节问题**/**人力问题)*

说到代码的优雅,我有时觉得使用错误码反而更简洁和优雅,假设我们有一个执行序列op1(); op2(); op3();中的任何一个都可能发生错
误,但希望它们互不影响,那么用错误码我们几乎不用怎么多写代码,如果是用异常,其面貌如下:
try {
op1();
} catch(...) {
}

try {
op2();
} catch(...) {
}

try {
op3();
} catch(...) {
}

这个例子在使用SmartPointer封装的COM接口时就可能会遇到
其原因是由于异常对代码执行流程的强制破坏造成的

>
> 11. *错误汇报方案不一致性*。看看Win32下面的错误汇报机制吧:HRESULT、BOOL、GetLastError...
> 本质上就是因为利用返回值通道是一个补丁方案,错误处理是程序的一个方面(aspect
> ),理应有其单独的汇报通道。利用异常的话,错误汇报方案就立即统一了,因为这是一个first-class的语言级支持机制。*(人力问题)*

汇报方式是统一了,但还是得考虑异常的继承体系问题,不过使用异常的一个好处是能够catch all而不管是具体是什么异常


>
> 异常与错误代码的*本质区别之四*--*异常是**OO**的*。
> 异常与错误代码的*本质区别之五*--*异常是强类型的*。
> 异常与错误代码的*本质区别之六*--*异常是**first-class**的语言机制*。

还有一个区别应该是异常自动改变执行代码流程,使异常自动向上传递

freefalcon

unread,
Oct 29, 2007, 11:07:52 AM10/29/07
to TopLanguage
关于错误或者异常的分类处理,在我的实际应用中,基本上都是分为两类:正常情况(Happy Path)和非正常情况,因此对于错误的判断往往只是使用
SUCCEDED或FAILED进行划分,很少有具体针对每种错误单独处理的时候(有,但很少),异常同样如此。因此,更多的错误值只是为了具体的表述
是哪种错误,以方便调试或提示。

不知大家的实践经验如何?

On 10月29日, 下午2时13分, pongba <pon...@gmail.com> wrote:
> All sounds reasonable:-)
>
> 只有一个问题:review过程也要花钱的,对不?具体地说,使用错误代码作为机制,那么有相当一部分review工作要花在"检查所有函数的所有返回值是否都 被check了",具体多少工作量我不知道。此外,考虑到代码修改过程中引入的问题:比如一个被调用的函数增加了一个错误返回代码,那么所有的调用方都需要重新 review一下,不管调用方是否是错误中立的。这是事实吧?但这个问题到底要紧到什么程度,我还是不知道。但有一点是肯定的,归根到底这是钱的问题。
> 那么既然是钱的问题,我就不禁要问了。培训人员也是钱的问题,对不?如果一开始的培训能够省掉过程中不必要的review部分(像我上面的提到的,显然,用异常 跟用错误代码,需要的review量是不一样的)。那么哪个更经济呢?
> 如果认为在一开始培训人员不经济,那么为什么不?是因为难度太大?为什么大?要specific的理由。主观的倾向还不够strong:-)


>
> On 10/29/07, wang xin <cber.wang...@gmail.com> wrote:
>
>
>
>
>
>
>
> > 在07-10-29,pongba <pon...@gmail.com> 写道:
>

> > > 现实问题:
>
> > > 1. 存在检查是否check了每个可能的错误返回值的工具吗?
>
> > 不知道,希望有
>
> > 2. 如果存在,你的项目用了吗?进一步,有多少现实的项目用了呢?(我知道在Joel 的项目中是通过编码规范来约束开发人员"一定要check
> > > 所有返回值"的)。
>
> > 因为1的不确定性答案,no
>
> > 3. 通过编码规范来约束,可靠吗?(或许总比依赖于程序员对异常这一语言特性的掌握程度可靠?)
>
> > 可靠,因为我们这边有着极其严格的code review过程(顺便说一下,获得高质量代码的最佳途径就是code review,其他的都是纸上谈兵)
>
> > 4. 如果没有这样的工具,在未来可能出现这样的工具吗?如果存在,可能普及吗?(我的认为是不可能,因为异常在目前的所有主流语言中都是唯一的正规军)。
>
> > 我想会的,因为从技术角度来看难度不大;但能不能广为流传,就不一定了;最有可能就是,某小牛在自己的team里面写了一个给大伙用
>
> > 5. 掌握异常这一语言真的很困难吗?"没有合格的开发人员"的问题的根源是什么?是因为这一语言特性真的很困难?还是其它什么原因。
>
> > 真的很难
> > ,"没有合格的开发人员"的问题根源就是,CS作为一门新兴的科学,没有足够的底蕴来制造出现在IT行业所需要的所有从业人员;语言特性的使用很简单,但细节太 多太零碎,导致用错的概率近乎100%
>
> > 最后:
>
> > > 如果你是项目主管,你是愿意在项目的一开始花钱培训程序员,教会他们如何正确使用异常。还是愿意在项目的整个过程中花钱(人力资源也是钱),以使得他们的确正确 使用了错误代码。这是一个决策问题。给出你的决策,并给出决策的经济学理由(不是主观理由),如果你选择A
> > > ,那么为什么你觉得A最经济?反之亦然。
>
> > 好吧,我选择做一个反面人物,我不愿意花钱培训程序员,也不愿意在项目中花钱让他们成长;培训这个过程应该在学校里面就结束了,工作中能够给以的培训只能是很有 限的。换而言之,我希望可以招到熟练工,如果招不到,那么就希望找几个机灵点的,可以在平时项目过程中被"潜移默化"的;如果让我专门花钱来培训他们,No
> > Way !!!!!


>
> > --
> > Everything is possible and available if we trust ourselves!
>
> --
> 刘未鹏(pongba)|C++的罗浮宫http://blog.csdn.net/pongba

> TopLanguagehttp://groups.google.com/group/pongba- 隐藏被引用文字 -
>
> - 显示引用的文字 -

freefalcon

unread,
Oct 29, 2007, 11:18:39 AM10/29/07
to TopLanguage

On 10月29日, 下午4时50分, pongba <pon...@gmail.com> wrote:


> On 10/29/07, wang xin <cber.wang...@gmail.com> wrote:
>
>
>
> > 在07-10-29,pongba <pon...@gmail.com> 写道:
>
> > > 当然,这个逻辑也有一个关键的漏洞:1. 如果review不存在百密一疏的话这个逻辑就不存在。2.
> > > 如果这类错误可以由其它路径排查(测试?),或者这类错误少到根本无伤大雅的程度的话,这逻辑也不成立。
>
> > 再次重申一下,review是最有效地找到代码问题的(终极?)手段
> > (这个论证是我以前看某调查文章里面提到的),如果review都发现不了的问题,基本上在实现人员的开发过程中都不会被发现也很难于被封装成异常抛出
>
> 我记得这里讨论的范畴应该是在错误传播过程中的代码;而非错误发生点上的代码。
> 可以假设,所有真正的错误,在发生点上都能够被抛出(不管是异常还是错误代码)。

> 而在错误传播过程中,我的意思是,使用错误代码的话,因为要层层手动传播,靠严格的review过程,也是不足够的,百密一疏的情况总会发生,而一旦发生,程序 必然会带着错误状态往下执行,轻则被动崩溃,重则毫无明显症状;我推测为什么大量软件会莫名其妙地被动崩溃("非法操作"),就是因为错误传播链里面除了疏忽, 一环疏忽,则全盘皆输。另一方面,再来看异常,异常的话,中间的传播链条是自动的,开发者只要管好stack-unwinding过程中的事情就好了。所以就避 免了因手动传播失误而导致的被动崩溃(被动崩溃是危险的,因为程序不再可控,什么情况都可能发生)。我推测Eclipse在错误发生情况下的良好表现就是因为异 常机制的好处,Eclipse用的时候也经常发生错误,但我的经验是都能体面inform用户和退出模块。如果用错误,可能还没等到错误代码传播到fault- barrier,就因为某个环节失误而不雅地崩溃了。

雅与不雅恐怕更多的是程序员自己的感受,对于用户来说都是一样的:程序怎么崩了?然后小心一个电话就打过来,问你什么时候能解决,而你又不能重现用户当
时的运行条件,那就只有焦头烂额了,:D

red...@gmail.com

unread,
Oct 29, 2007, 11:24:22 AM10/29/07
to pon...@googlegroups.com
freefalcon 写道:

> 关于错误或者异常的分类处理,在我的实际应用中,基本上都是分为两类:正常情况(Happy Path)和非正常情况,因此对于错误的判断往往只是使用
> SUCCEDED或FAILED进行划分,很少有具体针对每种错误单独处理的时候(有,但很少),异常同样如此。因此,更多的错误值只是为了具体的表述
> 是哪种错误,以方便调试或提示。
>
> 不知大家的实践经验如何?
>
真正的出错, 高层能做修复的机会其实很少, 例如配置文件格式都错了, 几乎是没
有办法修复的 (即使是使用内置缺省值替代, 从而本级吃掉这个错, 这其实没有对
下层错误进行修复, 只是本级遮盖而已).

所以

>>SUCCEDED或FAILED进行划分,很少有具体针对每种错误单独处理的时候(有,但很少)

大多数时候, 我觉得也是足够了.

只是有些时候, 底层认为可能是出错, 但是这种情况只是高层老早就认为是可能性之一, 本来就根据这个情况有不同的处理, 才有必要搞清楚下级到底发生了什么. 例如一个服务器群, 客户机 socket 层连接一个服务器超时连不上, 客户机更高一层可以选择另外一个重新连接, 此时高层就需要知道socket是连接超时错误呢, 还是根本就是 network down 等其他错误.


清风雨

unread,
Oct 29, 2007, 11:28:45 AM10/29/07
to TopLanguage
代码复查方法通常是必要的。我每次程序的问题,几乎都是我自己知道并解决的,很少有盲目测试覆盖到的。
在我以前帮同时分析代码逻辑时,也就半小时左右,就发现了一堆问题。但如果想覆盖测试,这个时间和人力是无法接受的。
还有有些多线程程序的错误,如果想用跑程序的方法几乎崩溃(当然,你能做到模块划分明确、设计合理,单元测试完备那是很好 -- 但,要做到谈何容易,
而且这个工作量和成本太巨大了)。

关于异常的说法,我觉得没必要弄的和返回值对立和你死我活的感觉。
至少在C里一直成功的返回值也没有那个项目因为没有异常就不错误百出了。就是在java这种完善异常机制的语言里,时常看到的一个丢给用户的异常和程序
崩溃也没什么两样。而在c++里,异常还是语言级的(java里好歹也是平台级别的,而且有明确的概念划分:runtime error/异常等),对
于系统级别的异常压根就傻眼。
再说编码便利上,关于fopen的(这个代码在上一贴的连接文字回帖里有老外的举例),或者socket接口等API的,异常反而麻烦(假设本身设计为
throw模式)--catch代码里面又抛除异常时,很容易进入一个循环陷阱(catch里throw,然后再catch再throw)。
说白了,他们都只是手法,为了编写软件而生。而完美的软件只是一个梦想。用了异常照样不能解决软件中的各种问题。根据具体的情况、成本选择恰当的方法才
是正确的。有时候不完美是一个很好的选择,比如我在写一个内部用的编辑器,我就基本不需要考虑类似谁把我依赖的配置文件给删除了(这里很适合用异常,但
适合的比较倾向系统层面,c++的异常一直给我鸡肋的感觉,而实际上这里我没有必要去做这个异常检查和处理--实际环境和需求决定。我在里面做了比较多
的错误处理判断(还不是异常),结果头上闲我化的时间太多,说我做的工作还不如一个已经存在的但基本一不小心就会崩溃的另一个程序。这种现象其实很普
遍。所以,完美理论和实际有时是需要脱节的)。

再说培训使用异常,那么同样也可以培训使用返回值。人员素质的提高显然并不是用来驳斥返回值的好理由。

我个人觉得,这两者都是剑,怎么用还得看人。而运用自如、存乎一心,方是正道。^_^

freefalcon

unread,
Oct 29, 2007, 12:02:28 PM10/29/07
to TopLanguage

On 10月29日, 下午11时24分, red...@gmail.com wrote:

>
> 只是有些时候, 底层认为可能是出错, 但是这种情况只是高层老早就认为是可能性之一, 本来就根据这个情况有不同的处理, 才有必要搞清楚下级到底发生了什么. 例如一个服务器群, 客户机 socket 层连接一个服务器超时连不上, 客户机更高一层可以选择另外一个重新连接, 此时高层就需要知道socket是连接超时错误呢, 还是根本就是 network down 等其他错误.

网络中的异常确实很多,也属于异常处理的重中之重,我写的网络程序中就有相当一部分代码用于处理连接重连、数据重发等。

说道这,我们不妨按照可恢复异常和不可恢复异常来讨论,这可能更有实际指导意义。
对于不可恢复的异常,其必然导致程序无法正常运行,那么解决问题的重点是如何尽早并准确的将问题反映出来。Question:你是如何处理这类问题的?
比如像前面提到的直接abort
对于可恢复的异常,其处理方式对于产品的健壮性才有着举足轻重的作用,因此,Question:你在实际中对哪些异常进行了恢复性处理,处理方式是怎样
的?前面的网路异常是一个典型例子。
附:你认为内存分配失败属于哪种异常,你通常是如何处理的?反正我基本上是对其无视,:P

莫华枫

unread,
Oct 29, 2007, 8:58:33 PM10/29/07
to pon...@googlegroups.com


在07-10-29,清风雨 <zhang...@gmail.com> 写道:
代码复查方法通常是必要的。我每次程序的问题,几乎都是我自己知道并解决的,很少有盲目测试覆盖到的。
在我以前帮同时分析代码逻辑时,也就半小时左右,就发现了一堆问题。但如果想覆盖测试,这个时间和人力是无法接受的。

第一、如果是盲目的测试,自然不会有像样的结果。测试有完整的理论和方法。如果能够建立完整的测试用例, 按照规程执行,那么理论上它是可以确定性地覆盖所有情况。尽管实践中无法做到,但是即便是一般情况下,也能做到90%以上。而且这90%是确定的,只要做就肯定能发现问题。review完全取决于人,及其状态,我们能认为这是确定性的吗?
第二、测试的作用不仅仅在于检测软件的错误处理情况,它同时也肩负了业务逻辑检验、流程检验、系统结构检验。它是多功能的。同时解决了诸多问题,效益更高。
第三、测试是可以自动化的。现在市面上成熟的测试工具非常完善,无论是行为测试,还是强度测试,都能做到。review能做到这样的自动化程度吗?
第四、即便review了,测试依然无法避免,其强度依然无法减弱。review成了点缀,对软件质量的贡献与其花费并不相称。
正因为如此,cmm中,测试是非常关键的practise,放在非常重要的位置。作为基础的东西,必须首先满足。而review则作为提高能力的东西,更多地用在改进软件开发过程中,而不是现实的开发工作中。即便是使用,也更侧重于对需求和设计的审查。code review则提倡在有条件的情况下实施。
但是,请注意,这里所涉及的review不是看一下某人的代码,找出一些毛病,而是一个严格的过程,要有相应的预备工作、记录,以及最后的问题报告。否则,我们无法确保一个问题被发现,并且被更正。这种要求完全为了确保获得(尽可能)确定性的结果。这种要求的确非常苛刻,但是如果做不到,那么review的作用也仅能停留在"替菜鸟找出编码时的毛病"层面上,无法真正起到提高软件质量的作用。
这里,8-2原则依然适用,即8成的问题在20%的时间里可以找到,而剩下的2成,则需要80%的时间。但从软件质量角度而言,这剩下的2成所造成的问题,时常会致命的。相比之下,在理论的指导下,测试找到剩下的2成的概率和效率,要远远高于review,因为测试是确定性的。



还有有些多线程程序的错误,如果想用跑程序的方法几乎崩溃(当然,你能做到模块划分明确、设计合理,单元测试完备那是很好 -- 但,要做到谈何容易,
而且这个工作量和成本太巨大了)。

关于异常的说法,我觉得没必要弄的和返回值对立和你死我活的感觉。
至少在C里一直成功的返回值也没有那个项目因为没有异常就不错误百出了。就是在java这种完善异常机制的语言里,时常看到的一个丢给用户的异常和程序
崩溃也没什么两样。

如果发生错误, 崩溃肯定比不声不响地带着问题往下走更好,是吧?一个软件中最珍贵的是什么?当然是数据。如果程序退出或崩溃,客户或许还会有耐心等你把问题解决掉。但是,一旦程序闷声不响地走下去,造成数据败坏,而无法挽回,客户的反应是谁都不想看到的吧。(更何况多数闷声不响的错误最终也会导致崩溃,是真正崩溃,而不是可控的、带清理能力的退出)。
关键是,如果一定要出错,那就让错误看得见。在error code模式下,被发现的错误(已返回error code)并不一定是可见的。但在异常中,错误只要被发现(异常抛出),必然最终是可见的。这一点远比错误看上去的形式来得重要。

而在c++里,异常还是语言级的(java里好歹也是平台级别的,而且有明确的概念划分:runtime error/异常等),对
于系统级别的异常压根就傻眼。
再说编码便利上,关于fopen的(这个代码在上一贴的连接文字回帖里有老外的举例),或者socket接口等API的,异常反而麻烦(假设本身设计为
throw模式)--catch代码里面又抛除异常时,很容易进入一个循环陷阱(catch里throw,然后再catch再throw)。
说白了,他们都只是手法,为了编写软件而生。而完美的软件只是一个梦想。用了异常照样不能解决软件中的各种问题。根据具体的情况、成本选择恰当的方法才
是正确的。有时候不完美是一个很好的选择,比如我在写一个内部用的编辑器,我就基本不需要考虑类似谁把我依赖的配置文件给删除了(这里很适合用异常,但
适合的比较倾向系统层面,c++的异常一直给我鸡肋的感觉,而实际上这里我没有必要去做这个异常检查和处理--实际环境和需求决定。我在里面做了比较多
的错误处理判断(还不是异常),结果头上闲我化的时间太多,说我做的工作还不如一个已经存在的但基本一不小心就会崩溃的另一个程序。这种现象其实很普
遍。所以,完美理论和实际有时是需要脱节的)。

再说培训使用异常,那么同样也可以培训使用返回值。人员素质的提高显然并不是用来驳斥返回值的好理由。

我个人觉得,这两者都是剑,怎么用还得看人。而运用自如、存乎一心,方是正道。^_^

zcpro

unread,
Oct 29, 2007, 10:28:46 PM10/29/07
to TopLanguage
>>5. *耦合*。
>>即便你的函数是一个异常中立的函数,不管底层传上来哪些错误一律抛给上层,你仍然需要在每个调用的边界检查,并妥 >>善往上手动传播每一个错误代码。而一旦底层接口增加、减少或改动错误代码的话,你的函数就需要立即作出相应改动, >>检查并传播底层接口改动后相应的错误代码

关于这一点,如果使用异常的话只是解决了问题的一半,还有一半在函数返回的错误代码的注释上,即使使用异常,该函数会抛出哪些异常总要有个地方说明吧,
我想这就是莫华枫说的选错文件程序都会崩掉的问题出现的原因。麻烦的是写这个说明并非想像中的那么简单。如果函数foo对异常中立,但是foo调用了函
数bar,而bar会抛出异常,但是foo的使用者并不知道它调用了bar,这个时候是否需要在foo的说明中重复一遍bar的异常说明?使用异常的同
学一般是怎么做的?一种粗略的做法是在类的说明中附带抛出异常的说明,但是使用这样的类总觉得提心吊胆,有种对每个成员函数都catch一把的冲动。

pongba

unread,
Oct 29, 2007, 10:39:01 PM10/29/07
to pon...@googlegroups.com
On 10/29/07, 清风雨 <zhang...@gmail.com> wrote:
代码复查方法通常是必要的。我每次程序的问题,几乎都是我自己知道并解决的,很少有盲目测试覆盖到的。
在我以前帮同时分析代码逻辑时,也就半小时左右,就发现了一堆问题。但如果想覆盖测试,这个时间和人力是无法接受的。
还有有些多线程程序的错误,如果想用跑程序的方法几乎崩溃(当然,你能做到模块划分明确、设计合理,单元测试完备那是很好 -- 但,要做到谈何容易,
而且这个工作量和成本太巨大了)。

关于异常的说法,我觉得没必要弄的和返回值对立和你死我活的感觉。

本来就没有你死我活啊,两者都是完全可行的错误处理机制。我们讨论的是哪种方法更好。这里好的定义是:代码的可读性、可维护性。代码的编写代价。甚至合格程序员的可得性。

至少在C里一直成功的返回值也没有那个项目因为没有异常就不错误百出了。就是在java这种完善异常机制的语言里,时常看到的一个丢给用户的异常和程序
崩溃也没什么两样。而在c++里,异常还是语言级的(java里好歹也是平台级别的,而且有明确的概念划分:runtime error/异常等),对
于系统级别的异常压根就傻眼。

没错,平台级的异常的确有本质上的优势。

再说编码便利上,关于fopen的(这个代码在上一贴的连接文字回帖里有老外的举例),或者socket接口等API的,异常反而麻烦(假设本身设计为
throw模式)--catch代码里面又抛除异常时,很容易进入一个循环陷阱(catch里throw,然后再catch再throw)。

这个逻辑我不理解;)

说白了,他们都只是手法,为了编写软件而生。而完美的软件只是一个梦想。

没有谁想要完美。工程学的问题本来往往就是不完美的。

用了异常照样不能解决软件中的各种问题。根据具体的情况、成本选择恰当的方法才
是正确的。

这正是我们讨论的。哪个成本更低。

有时候不完美是一个很好的选择,比如我在写一个内部用的编辑器,我就基本不需要考虑类似谁把我依赖的配置文件给删除了(这里很适合用异常,但
适合的比较倾向系统层面,c++的异常一直给我鸡肋的感觉,而实际上这里我没有必要去做这个异常检查和处理--实际环境和需求决定。我在里面做了比较多
的错误处理判断(还不是异常),结果头上闲我化的时间太多,说我做的工作还不如一个已经存在的但基本一不小心就会崩溃的另一个程序。这种现象其实很普
遍。所以,完美理论和实际有时是需要脱节的)。

这个逻辑我也不理解..你的意思是,内部用编辑器就无需考虑unhappy path? 这个当然是可以的。但这其实是把异常放在一个他不属于的地方去讨论他的优劣。本来我们讨论的场地就是那些需要考虑unhappy path的地方,否则讨论一个对付unhappy path的工具还有什么意义呢?

再说培训使用异常,那么同样也可以培训使用返回值。人员素质的提高显然并不是用来驳斥返回值的好理由。

没错。而且返回值的话就基本不用培训。使用返回值是把代价摊到过程中的review和编码心力上,摊到软件的可靠性上(当然,前提是这个软件的确需要某种程度上的健壮性)。

我个人觉得,这两者都是剑,怎么用还得看人。而运用自如、存乎一心,方是正道。^_^

各大五十大板.. 《悟空传》里说"母猪还有个美丑呢" :-)

Atry

unread,
Oct 30, 2007, 1:25:23 AM10/30/07
to pon...@googlegroups.com


在07-10-30,pongba <pon...@gmail.com> 写道:


On 10/29/07, 清风雨 <zhang...@gmail.com > wrote:
代码复查方法通常是必要的。我每次程序的问题,几乎都是我自己知道并解决的,很少有盲目测试覆盖到的。
在我以前帮同时分析代码逻辑时,也就半小时左右,就发现了一堆问题。但如果想覆盖测试,这个时间和人力是无法接受的。
还有有些多线程程序的错误,如果想用跑程序的方法几乎崩溃(当然,你能做到模块划分明确、设计合理,单元测试完备那是很好 -- 但,要做到谈何容易,
而且这个工作量和成本太巨大了)。

关于异常的说法,我觉得没必要弄的和返回值对立和你死我活的感觉。

本来就没有你死我活啊,两者都是完全可行的错误处理机制。我们讨论的是哪种方法更好。这里好的定义是:代码的可读性、可维护性。代码的编写代价。甚至合格程序员的可得性。

至少在C里一直成功的返回值也没有那个项目因为没有异常就不错误百出了。就是在java这种完善异常机制的语言里,时常看到的一个丢给用户的异常和程序
崩溃也没什么两样。而在c++里,异常还是语言级的(java里好歹也是平台级别的,而且有明确的概念划分:runtime error/异常等),对
于系统级别的异常压根就傻眼。

没错,平台级的异常的确有本质上的优势。

再说编码便利上,关于fopen的(这个代码在上一贴的连接文字回帖里有老外的举例),或者socket接口等API的,异常反而麻烦(假设本身设计为
throw模式)--catch代码里面又抛除异常时,很容易进入一个循环陷阱(catch里throw,然后再catch再throw)。

这个逻辑我不理解;)

这个说的是 Java 里面大部分 close() 都会抛异常

abware

unread,
Nov 6, 2007, 4:16:35 AM11/6/07
to TopLanguage
不愿意花钱培训未必是最经济的。
管理说到底是人的问题,如果你的程序员在你的项目中得不到提高,那他会在你这呆多久?"机灵点"的孩子们更不愿意做编码机器。
此外低水平的编码会导致维护工作量巨大,相应的成本也更高。有些培训也不用花很多钱的。

>
>
> > 如果你是项目主管,你是愿意在项目的一开始花钱培训程序员,教会他们如何正确使用异常。还是愿意在项目的整个过程中花钱(人力资源也是钱),以使得他们的确正确 使用了错误代码。这是一个决策问题。给出你的决策,并给出决策的经济学理由(不是主观理由),如果你选择A
> > ,那么为什么你觉得A最经济?反之亦然。
>
> 好吧,我选择做一个反面人物,我不愿意花钱培训程序员,也不愿意在项目中花钱让他们成长;培训这个过程应该在学校里面就结束了,工作中能够给以的培训只能是很有 限的。换而言之,我希望可以招到熟练工,如果招不到,那么就希望找几个机灵点的,可以在平时项目过程中被"潜移默化"的;如果让我专门花钱来培训他们,No
> Way !!!!!
>
>
>

Wang Xin

unread,
Nov 6, 2007, 4:59:16 AM11/6/07
to pon...@googlegroups.com


在07-11-6,abware <abw...@gmail.com> 写道:
不愿意花钱培训未必是最经济的。
管理说到底是人的问题,如果你的程序员在你的项目中得不到提高,那他会在你这呆多久?"机灵点"的孩子们更不愿意做编码机器。
此外低水平的编码会导致维护工作量巨大,相应的成本也更高。有些培训也不用花很多钱的。

>
果然不出所料地被bs了:(
基本上来说,我们公司平时的流程就足以抵过培训的功效了(至少是我所知道的90%以上的培训);如果小朋友真的有心的话,从中得到的提升比参加一个莫名其妙的培训要好多了
再说了,留住人的理由很多:金钱、技术、前途等;不是所有的人都唯技术至上的

怀宇范

unread,
Nov 6, 2007, 6:08:02 AM11/6/07
to pon...@googlegroups.com
对于高层来说,我觉得很多时候提供丰富的信息是很有帮助的。特别是做类库或二次开发平台一级的开发的时候。需要提供足够的信息帮助其他人进行使用和调试,并且你不能预测别人的行为。比如配置文件格式错了通常没有解决办法,但你上层的开发人员知道几种可能出现的错误,并打算进行恢复,如果你不提供一定的信息,他们就只能从头自己做了。。。





{
   if(you.GoTo("14#344, Tsinghua") && you.Find("Fan Huaiyu"))return true;
   if(you.MailTo(" dugu...@gmail.com ") || you.MailTo(" fan...@mails.tsinghua.edu.cn "))return true;
   if(you.PhoneTo("13488810330") || you.PhoneTo("01062778689"))return true;
   if(you.QQTo("120628812") || you.MSNTo("dugu...@hotmail.com"))return true;
   if(you.NetTo(" www.cnblogs.com/duguguiyu "))return true;
   return false;
}

abware

unread,
Nov 9, 2007, 6:20:58 AM11/9/07
to TopLanguage


你所说的流程也相当于是培训的一种,培训并不是非得开个培训班,培训应该是指使人在能力上有所提升的所有办法。
另外如果意愿花钱留人,当然没问题,问题是花多少钱?是否花得比培训还多?
前途就更虚了,凭什么说你这的前途比别处好?还不是因为更有钱景?做为被雇的人来说,技术至少是一个保证前途的有力武器。
改做管理也要学管理方面的技能,改做市场也要学市场方面的技能。

Reply all
Reply to author
Forward
0 new messages