标题党了一下: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. 掌握异常这一语言真的很困难吗?"没有合格的开发人员"的问题的根源是什么?是因为这一语言特性真的很困难?还是其它什么原因。
最后:
如果你是项目主管,你是愿意在项目的一开始花钱培训程序员,教会他们如何正确使用异常。还是愿意在项目的整个过程中花钱(人力资源也是钱),以使得他们的确正确使用了错误代码。这是一个决策问题。给出你的决策,并给出决策的经济学理由(不是主观理由),如果你选择 A,那么为什么你觉得A最经济?反之亦然。
2. 如果存在,你的项目用了吗?进一步,有多少现实的项目用了呢?(我知道在Joel 的项目中是通过编码规范来约束开发人员"一定要check所有返回值"的)。
3. 通过编码规范来约束,可靠吗?(或许总比依赖于程序员对异常这一语言特性的掌握程度可靠?)
4. 如果没有这样的工具,在未来可能出现这样的工具吗?如果存在,可能普及吗?(我的认为是不可能,因为异常在目前的所有主流语言中都是唯一的正规军)。
5. 掌握异常这一语言真的很困难吗?"没有合格的开发人员"的问题的根源是什么?是因为这一语言特性真的很困难?还是其它什么原因。
最后:
如果你是项目主管,你是愿意在项目的一开始花钱培训程序员,教会他们如何正确使用异常。还是愿意在项目的整个过程中花钱(人力资源也是钱),以使得他们的确正确使用了错误代码。这是一个决策问题。给出你的决策,并给出决策的经济学理由(不是主观理由),如果你选择 A,那么为什么你觉得A最经济?反之亦然。
好吧,我选择做一个反面人物,我不愿意花钱培训程序员,也不愿意在项目中花钱让他们成长;培训这个过程应该在学校里面就结束了,工作中能够给以的培训只能是很有限的。换而言之,我希望可以招到熟练工,如果招不到,那么就希望找几个机灵点的,可以在平时项目过程中被"潜移默化"的;如果让我专门花钱来培训他们,No Way !!!!!
All sounds reasonable:-)
只有一个问题:review过程也要花钱的,对不?具体地说,使用错误代码作为机制,那么有相当一部分review工作要花在"检查所有函数的所有返回值是否都被check了",具体多少工作量我不知道。此外,考虑到代码修改过程中引入的问题:比如一个被调用的函数增加了一个错误返回代码,那么所有的调用方都需要重新review一下,不管调用方是否是错误中立的。这是事实吧?但这个问题到底要紧到什么程度,我还是不知道。但有一点是肯定的,归根到底这是钱的问题。
那么既然是钱的问题,我就不禁要问了。培训人员也是钱的问题,对不?如果一开始的培训能够省掉过程中不必要的review部分(像我上面的提到的,显然,用异常跟用错误代码,需要的review量是不一样的)。那么哪个更经济呢?
如果认为在一开始培训人员不经济,那么为什么不?是因为难度太大?为什么大?要specific的理由。主观的倾向还不够strong:-)
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
我们公司通过了cmm3级,制定了严格的过程。所有过程中,什么都能够想办法做到,唯独peer review一项,根本无法坚持下去。因为成本太高了。
review一次,需要至少看一遍代码,同时还需要编码的人介绍一些相关的内容。这样至少要准备近一个星期。而且,即便这样,review的人也无法完全地找出问题。我们几个老鸟也经常让其他人的代码"带进沟里"。这就要求不只一个人,背靠背地review代码。这样,两三个人,个把星期的时间,而且还不止一次。这种成本对于多数企业是无法承受的。
所以,仅尝试了一两次,peer review就变成了"过一遍"。没有产生任何实际效果。
review对于需求最有用,其次是设计,但代码review是得不偿失的。相比之下,把这些资源用于测试,会得到更好的效果。而且测试解决了包括错误处理在内的很多问题。我们不可能因为做了review而削弱测试,这种风险是无法承担的。
但在测试中,异常比错误码更加"显眼",更容易发现问题。
这句话听起来很有力啊:)但在测试中,异常比错误码更加"显眼",更容易发现问题。
说句题外话。:P这句话听起来很有力啊:)但在测试中,异常比错误码更加"显眼",更容易发现问题。
有时,事情总是充满矛盾。虽说这是好事,但是,我们的一些程序员却由此养成了一个坏习惯。反正异常总会被捕捉到,那也没必要费心去处理这些异常。于是,除了那些需要进行业务处理的异常,其他的都一律不捕捉。结果就是时不时地跳出个系统级异常对话框,客户看着一头雾水。电话打过来,我们态度诚恳地告诉他选错了文件,客户也就做罢了。
有时我还真想给我们的客户写封感谢信,对他们的这种宽容和忍耐加以褒奖。(或许应该送面锦旗?)
唉,为什么好东西总是被人乱用啊。:(
On 10/29/07, 莫华枫 <longsh...@gmail.com > wrote:我们公司通过了cmm3级,制定了严格的过程。所有过程中,什么都能够想办法做到,唯独peer review一项,根本无法坚持下去。因为成本太高了。
review一次,需要至少看一遍代码,同时还需要编码的人介绍一些相关的内容。这样至少要准备近一个星期。而且,即便这样,review的人也无法完全地找出问题。我们几个老鸟也经常让其他人的代码"带进沟里"。这就要求不只一个人,背靠背地review代码。这样,两三个人,个把星期的时间,而且还不止一次。这种成本对于多数企业是无法承受的。
所以,仅尝试了一两次,peer review就变成了"过一遍"。没有产生任何实际效果。
review对于需求最有用,其次是设计,但代码review是得不偿失的。相比之下,把这些资源用于测试,会得到更好的效果。而且测试解决了包括错误处理在内的很多问题。我们不可能因为做了review而削弱测试,这种风险是无法承担的。
cber却说review烧钱没这么多,而且效果也比这要好。矛盾出现。
cber? :P
这句话听起来很有力啊:)但在测试中,异常比错误码更加"显眼",更容易发现问题。
在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看)
这句话听起来很有力啊:)但在测试中,异常比错误码更加"显眼",更容易发现问题。
"显眼 ":| 大部分的"显眼"的问题都不是问题,不显眼的才是
--
Everything is possible and available if we trust ourselves!
在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!
--
反者道之动,弱者道之用
m...@seaskysh.com
longsh...@gmail.com
http://blog.csdn.net/longshanks/
当然,这个逻辑也有一个关键的漏洞:1. 如果review不存在百密一疏的话这个逻辑就不存在。2. 如果这类错误可以由其它路径排查(测试?),或者这类错误少到根本无伤大雅的程度的话,这逻辑也不成立。
On Oct 29, 3:36 pm, "莫华枫" <longshank...@gmail.com> wrote:
> > 但在测试中,异常比错误码更加"显眼",更容易发现问题。
>
> > 这句话听起来很有力啊:)
>
> 说句题外话。:P
> 有时,事情总是充满矛盾。虽说这是好事,但是,我们的一些程序员却由此养成了一个坏习惯。反正异常总会被捕捉到,那也没必要费心去处理这些异常。于是,除了那些需要进行业务处理的异常,其他的都一律不捕捉。结果就是时不时地跳出个系统级异常对话框,客户看着一头雾水。电话打过来,我们态度诚恳地告诉他选错了文件,客户也就做罢了。
> 有时我还真想给我们的客户写封感谢信,对他们的这种宽容和忍耐加以褒奖。(或许应该送面锦旗?)
> 唉,为什么好东西总是被人乱用啊。:(
>
> --
> 反者道之动,弱者道之用
> m...@seaskysh.com
> longshank...@gmail.comhttp://blog.csdn.net/longshanks/
在07-10-29,pongba <pon...@gmail.com > 写道:
当然,这个逻辑也有一个关键的漏洞:1. 如果review不存在百密一疏的话这个逻辑就不存在。2. 如果这类错误可以由其它路径排查(测试?),或者这类错误少到根本无伤大雅的程度的话,这逻辑也不成立。
再次重申一下,review是最有效地找到代码问题的(终极?)手段 (这个论证是我以前看某调查文章里面提到的),如果review都发现不了的问题,基本上在实现人员的开发过程中都不会被发现也很难于被封装成异常抛出
--
Everything is possible and available if we trust ourselves!
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
嗯,这么说的话,用什么方式传播错误其实不重要?
On 10/29/07, red...@gmail.com <red...@gmail.com> wrote:意思就是大部分错误都是编程错误?好吧,我想作为一个程序员,我想大家都有体会的是,大多数错误(那些需要debug的),当然都是编程错误。
> 我推测为什么大量软件会莫名其妙地被动崩溃("非法操作"),就是因为错误传
> 播链里面除了疏忽,一环疏忽,则全盘皆输。
我自己的经历是, 如果程序员还说得过去, 大多数错误主要还是在发生地没有被发
现(多数设计或者编码错误, 修改之后未必需要引入一个runtime 错误), 而不是在
错误传播链中的问题.
然而,关键是,"异常OR错误代码"的这个争论,本来针对的问题领域就是"剩下来的小部分错误",即那些unexpected condition。这些condition也许连1%都不到,但却关乎软件在极端条件下的健壮性。比如内存耗尽这种问题,真是千载难逢,那么是不是意味 着就不用考虑了呢?(的确也有许多时候不用考虑,还是看软件的应用领域)。
> 相反,多线程就做不到了。搞一个解引用空指针,访问一个非法内存,整个进程
> 都跟着遭殃。不管那个非法动作处于哪个模块。 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 写道:
> 我也扯句题外话,哈哈:-)
> 云风曾经提倡过使用多进程模型,为什么?因为多进程更健壮,可以完全隔离错
> 误。一个进程随便怎么把工作给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 了, 等等, 所以, 能够正常跑起来之后, 再碰到严重错误的情形应该不多,
但是一旦碰到, 可能就是类似磁盘耗尽, 内存耗尽这种极端了, 此时, 除了记录错
误退出, 没有太多事情好做 ----- 或者帮用户杀掉几个进程, 删除几个文件来恢
复一下 ;)
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**的语言机制*。
还有一个区别应该是异常自动改变执行代码流程,使异常自动向上传递
不知大家的实践经验如何?
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- 隐藏被引用文字 -
>
> - 显示引用的文字 -
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
所以
>>SUCCEDED或FAILED进行划分,很少有具体针对每种错误单独处理的时候(有,但很少)
大多数时候, 我觉得也是足够了.
只是有些时候, 底层认为可能是出错, 但是这种情况只是高层老早就认为是可能性之一, 本来就根据这个情况有不同的处理, 才有必要搞清楚下级到底发生了什么. 例如一个服务器群, 客户机 socket 层连接一个服务器超时连不上, 客户机更高一层可以选择另外一个重新连接, 此时高层就需要知道socket是连接超时错误呢, 还是根本就是 network down 等其他错误.
关于异常的说法,我觉得没必要弄的和返回值对立和你死我活的感觉。
至少在C里一直成功的返回值也没有那个项目因为没有异常就不错误百出了。就是在java这种完善异常机制的语言里,时常看到的一个丢给用户的异常和程序
崩溃也没什么两样。而在c++里,异常还是语言级的(java里好歹也是平台级别的,而且有明确的概念划分:runtime error/异常等),对
于系统级别的异常压根就傻眼。
再说编码便利上,关于fopen的(这个代码在上一贴的连接文字回帖里有老外的举例),或者socket接口等API的,异常反而麻烦(假设本身设计为
throw模式)--catch代码里面又抛除异常时,很容易进入一个循环陷阱(catch里throw,然后再catch再throw)。
说白了,他们都只是手法,为了编写软件而生。而完美的软件只是一个梦想。用了异常照样不能解决软件中的各种问题。根据具体的情况、成本选择恰当的方法才
是正确的。有时候不完美是一个很好的选择,比如我在写一个内部用的编辑器,我就基本不需要考虑类似谁把我依赖的配置文件给删除了(这里很适合用异常,但
适合的比较倾向系统层面,c++的异常一直给我鸡肋的感觉,而实际上这里我没有必要去做这个异常检查和处理--实际环境和需求决定。我在里面做了比较多
的错误处理判断(还不是异常),结果头上闲我化的时间太多,说我做的工作还不如一个已经存在的但基本一不小心就会崩溃的另一个程序。这种现象其实很普
遍。所以,完美理论和实际有时是需要脱节的)。
再说培训使用异常,那么同样也可以培训使用返回值。人员素质的提高显然并不是用来驳斥返回值的好理由。
我个人觉得,这两者都是剑,怎么用还得看人。而运用自如、存乎一心,方是正道。^_^
>
> 只是有些时候, 底层认为可能是出错, 但是这种情况只是高层老早就认为是可能性之一, 本来就根据这个情况有不同的处理, 才有必要搞清楚下级到底发生了什么. 例如一个服务器群, 客户机 socket 层连接一个服务器超时连不上, 客户机更高一层可以选择另外一个重新连接, 此时高层就需要知道socket是连接超时错误呢, 还是根本就是 network down 等其他错误.
网络中的异常确实很多,也属于异常处理的重中之重,我写的网络程序中就有相当一部分代码用于处理连接重连、数据重发等。
说道这,我们不妨按照可恢复异常和不可恢复异常来讨论,这可能更有实际指导意义。
对于不可恢复的异常,其必然导致程序无法正常运行,那么解决问题的重点是如何尽早并准确的将问题反映出来。Question:你是如何处理这类问题的?
比如像前面提到的直接abort
对于可恢复的异常,其处理方式对于产品的健壮性才有着举足轻重的作用,因此,Question:你在实际中对哪些异常进行了恢复性处理,处理方式是怎样
的?前面的网路异常是一个典型例子。
附:你认为内存分配失败属于哪种异常,你通常是如何处理的?反正我基本上是对其无视,:P
代码复查方法通常是必要的。我每次程序的问题,几乎都是我自己知道并解决的,很少有盲目测试覆盖到的。
在我以前帮同时分析代码逻辑时,也就半小时左右,就发现了一堆问题。但如果想覆盖测试,这个时间和人力是无法接受的。
还有有些多线程程序的错误,如果想用跑程序的方法几乎崩溃(当然,你能做到模块划分明确、设计合理,单元测试完备那是很好 -- 但,要做到谈何容易,
而且这个工作量和成本太巨大了)。
关于异常的说法,我觉得没必要弄的和返回值对立和你死我活的感觉。
至少在C里一直成功的返回值也没有那个项目因为没有异常就不错误百出了。就是在java这种完善异常机制的语言里,时常看到的一个丢给用户的异常和程序
崩溃也没什么两样。
而在c++里,异常还是语言级的(java里好歹也是平台级别的,而且有明确的概念划分:runtime error/异常等),对
于系统级别的异常压根就傻眼。
再说编码便利上,关于fopen的(这个代码在上一贴的连接文字回帖里有老外的举例),或者socket接口等API的,异常反而麻烦(假设本身设计为
throw模式)--catch代码里面又抛除异常时,很容易进入一个循环陷阱(catch里throw,然后再catch再throw)。
说白了,他们都只是手法,为了编写软件而生。而完美的软件只是一个梦想。用了异常照样不能解决软件中的各种问题。根据具体的情况、成本选择恰当的方法才
是正确的。有时候不完美是一个很好的选择,比如我在写一个内部用的编辑器,我就基本不需要考虑类似谁把我依赖的配置文件给删除了(这里很适合用异常,但
适合的比较倾向系统层面,c++的异常一直给我鸡肋的感觉,而实际上这里我没有必要去做这个异常检查和处理--实际环境和需求决定。我在里面做了比较多
的错误处理判断(还不是异常),结果头上闲我化的时间太多,说我做的工作还不如一个已经存在的但基本一不小心就会崩溃的另一个程序。这种现象其实很普
遍。所以,完美理论和实际有时是需要脱节的)。
再说培训使用异常,那么同样也可以培训使用返回值。人员素质的提高显然并不是用来驳斥返回值的好理由。
我个人觉得,这两者都是剑,怎么用还得看人。而运用自如、存乎一心,方是正道。^_^
关于这一点,如果使用异常的话只是解决了问题的一半,还有一半在函数返回的错误代码的注释上,即使使用异常,该函数会抛出哪些异常总要有个地方说明吧,
我想这就是莫华枫说的选错文件程序都会崩掉的问题出现的原因。麻烦的是写这个说明并非想像中的那么简单。如果函数foo对异常中立,但是foo调用了函
数bar,而bar会抛出异常,但是foo的使用者并不知道它调用了bar,这个时候是否需要在foo的说明中重复一遍bar的异常说明?使用异常的同
学一般是怎么做的?一种粗略的做法是在类的说明中附带抛出异常的说明,但是使用这样的类总觉得提心吊胆,有种对每个成员函数都catch一把的冲动。
代码复查方法通常是必要的。我每次程序的问题,几乎都是我自己知道并解决的,很少有盲目测试覆盖到的。
在我以前帮同时分析代码逻辑时,也就半小时左右,就发现了一堆问题。但如果想覆盖测试,这个时间和人力是无法接受的。
还有有些多线程程序的错误,如果想用跑程序的方法几乎崩溃(当然,你能做到模块划分明确、设计合理,单元测试完备那是很好 -- 但,要做到谈何容易,
而且这个工作量和成本太巨大了)。
关于异常的说法,我觉得没必要弄的和返回值对立和你死我活的感觉。
至少在C里一直成功的返回值也没有那个项目因为没有异常就不错误百出了。就是在java这种完善异常机制的语言里,时常看到的一个丢给用户的异常和程序
崩溃也没什么两样。而在c++里,异常还是语言级的(java里好歹也是平台级别的,而且有明确的概念划分:runtime error/异常等),对
于系统级别的异常压根就傻眼。
再说编码便利上,关于fopen的(这个代码在上一贴的连接文字回帖里有老外的举例),或者socket接口等API的,异常反而麻烦(假设本身设计为
throw模式)--catch代码里面又抛除异常时,很容易进入一个循环陷阱(catch里throw,然后再catch再throw)。
说白了,他们都只是手法,为了编写软件而生。而完美的软件只是一个梦想。
用了异常照样不能解决软件中的各种问题。根据具体的情况、成本选择恰当的方法才
是正确的。
有时候不完美是一个很好的选择,比如我在写一个内部用的编辑器,我就基本不需要考虑类似谁把我依赖的配置文件给删除了(这里很适合用异常,但
适合的比较倾向系统层面,c++的异常一直给我鸡肋的感觉,而实际上这里我没有必要去做这个异常检查和处理--实际环境和需求决定。我在里面做了比较多
的错误处理判断(还不是异常),结果头上闲我化的时间太多,说我做的工作还不如一个已经存在的但基本一不小心就会崩溃的另一个程序。这种现象其实很普
遍。所以,完美理论和实际有时是需要脱节的)。
再说培训使用异常,那么同样也可以培训使用返回值。人员素质的提高显然并不是用来驳斥返回值的好理由。
我个人觉得,这两者都是剑,怎么用还得看人。而运用自如、存乎一心,方是正道。^_^
On 10/29/07, 清风雨 <zhang...@gmail.com > wrote:代码复查方法通常是必要的。我每次程序的问题,几乎都是我自己知道并解决的,很少有盲目测试覆盖到的。
在我以前帮同时分析代码逻辑时,也就半小时左右,就发现了一堆问题。但如果想覆盖测试,这个时间和人力是无法接受的。
还有有些多线程程序的错误,如果想用跑程序的方法几乎崩溃(当然,你能做到模块划分明确、设计合理,单元测试完备那是很好 -- 但,要做到谈何容易,
而且这个工作量和成本太巨大了)。
关于异常的说法,我觉得没必要弄的和返回值对立和你死我活的感觉。
本来就没有你死我活啊,两者都是完全可行的错误处理机制。我们讨论的是哪种方法更好。这里好的定义是:代码的可读性、可维护性。代码的编写代价。甚至合格程序员的可得性。至少在C里一直成功的返回值也没有那个项目因为没有异常就不错误百出了。就是在java这种完善异常机制的语言里,时常看到的一个丢给用户的异常和程序
崩溃也没什么两样。而在c++里,异常还是语言级的(java里好歹也是平台级别的,而且有明确的概念划分:runtime error/异常等),对
于系统级别的异常压根就傻眼。
没错,平台级的异常的确有本质上的优势。再说编码便利上,关于fopen的(这个代码在上一贴的连接文字回帖里有老外的举例),或者socket接口等API的,异常反而麻烦(假设本身设计为
throw模式)--catch代码里面又抛除异常时,很容易进入一个循环陷阱(catch里throw,然后再catch再throw)。
这个逻辑我不理解;)
>
>
> > 如果你是项目主管,你是愿意在项目的一开始花钱培训程序员,教会他们如何正确使用异常。还是愿意在项目的整个过程中花钱(人力资源也是钱),以使得他们的确正确 使用了错误代码。这是一个决策问题。给出你的决策,并给出决策的经济学理由(不是主观理由),如果你选择A
> > ,那么为什么你觉得A最经济?反之亦然。
>
> 好吧,我选择做一个反面人物,我不愿意花钱培训程序员,也不愿意在项目中花钱让他们成长;培训这个过程应该在学校里面就结束了,工作中能够给以的培训只能是很有 限的。换而言之,我希望可以招到熟练工,如果招不到,那么就希望找几个机灵点的,可以在平时项目过程中被"潜移默化"的;如果让我专门花钱来培训他们,No
> Way !!!!!
>
>
>
不愿意花钱培训未必是最经济的。
管理说到底是人的问题,如果你的程序员在你的项目中得不到提高,那他会在你这呆多久?"机灵点"的孩子们更不愿意做编码机器。
此外低水平的编码会导致维护工作量巨大,相应的成本也更高。有些培训也不用花很多钱的。
>
{
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;
}
你所说的流程也相当于是培训的一种,培训并不是非得开个培训班,培训应该是指使人在能力上有所提升的所有办法。
另外如果意愿花钱留人,当然没问题,问题是花多少钱?是否花得比培训还多?
前途就更虚了,凭什么说你这的前途比别处好?还不是因为更有钱景?做为被雇的人来说,技术至少是一个保证前途的有力武器。
改做管理也要学管理方面的技能,改做市场也要学市场方面的技能。