如何编写健壮的程序

以下紧为个人观点,希望大家讨论和完善,并设计较为好用的类库


编写健壮程序,首先正确的理解类库,
考虑异常处理和输入参数校验,其次是跟踪和改进。

概念
程序的执行就是系统状态的变化过程, 任何一个方法的执行,对象和系统会进入下一个状态。错误的发生可归结为:
 参数错误
 状态错误,方法执行的前提条件没有得到满足
这些处理,在公有的(public)方法或类和非公有的方法或类处理中是不一样的,比如在私有的方法中,一般不用有用参数校验,因为它的调用者通常是程序的编写者自己。但输入参数是否为空对象,可以在调用之前进行诊断,然后进行相应的处理 (或者不判断但系统会引发运行时错误)。

参数校验一般是指用户输入和应用程序的调用时的参数输入校验,前者应该为程序必须处理的可恢复的异常,后者通常导致程序的执行逻辑产生的运行时异常,如典型的数组越界和空指针, 一般在程序的设计中有一般的处理方式,在java中抛出IllegalArgumentException或进行错误翻译到适当的抽象层次进行抛出。

异常一般有三种级别:
 可恢复,被检查的异常,这类是调用应用程序必须处理的,我们写程序处理的多半为这种异常。
 运行时错误,绝大部分为不可恢复的异常。此种异常的抛出时,通常调用的功能不能成功的执行,但不是严重的,应用程序不会崩溃。这类异常的处理就非常重要,它会影响你程序的失败范围。
 非常严重的错误,它可能会导致整个应用程序崩溃,在JAVA中通常抛出为ERROR类型错误,通常的起因为资源枯竭,环境严重错误。

对于在应用程序的体系结构设计中,其传播途径和处理方式,大家要达成共识,
首先为要把异常和正常区分开来,一个抛出太多异常的接口,只会使程序过度复杂,一些需要处理的异常也不能加重别人的负担。其次对用户的输入校验应该作为被检查的异常来处理,它是用户可能出现的一种情况,也是用户输入和输出接口的一部分,这类处理抛出的错误信息集中保存便于修改,并使用统一的接口进行读取,并放在公共的包类大家一起使用。
比如在apache的java项目axis开发中Developer's Guide 建议大家如此使用
Handle Specific Exceptions in Inner Code
Catch All Exceptions in Outermost Flow of Control
Catching and Logging Exceptions

下表为JAVA中最常用的异常类:
Table 1。 Commonly Used Exceptions
Exception Occasion for Use
IllegalArgumentException 非法参数
IllegalStateException 对象非法状态
NullPointerException 空值参数
IndexOutOfBoundsException 下标越界
UnsupportedOperationException 没有实现的方法。

原则
以下为异常处理的基本原则:
1. Use exceptions only for exceptional conditions
只对异常情况使用异常处理,不要使用异常实现控制结构,对于经常发生的可预计事件不要采用异常
2. Use checked exceptions for recoverable conditions and run-time exceptions for programming errors
在可恢复的情况下抛出异常,程序错误使用运行时异常
3. Avoid unnecessary use of checked exceptions
避免过多的不必要的被检查的异常
4. Favor the use of standard exceptions
尽量使用标准异常
5. Throw exceptions appropriate to the abstraction
异常的抛出有正常的抽象级别
6. Document all exceptions thrown by each method
使用文档记录抛出的异常,例如JAVA DOC @exception name des cription
7. Include failure-capture information in detail messages
包括错误的详细信息
8. Strive for vetbfailure atomicity
使失败原子性,不要让程序在不正确的状态,比如出错事务回滚
9. Don't ignore exceptions
不要忽视(不处理)异常

设计
设计的目标为统一和规范化错误处理的流程,设计统一的共用类,大家进行统一调度。为了达到以上目标使用如下的原则:
1 错误的详细信息统一保存
2 用户的输入校验,网页只处理是否为空和去掉头尾空格,客户断逻辑进行类型转换,业务逻辑层进行校验,然后查询错误信息,使它成为最终的详细的错误信息返回给用户。
3异常的产生,调用者对非用户输入参数校验和状态错误产生的异常,使用第5和第7条规则
错误信息表
4 当然跟踪和诊断也是不可少的

程序的健壮性还要从设计角度考虑:
1. 粒度必须细化,正所谓,一粒沙土要比沙土做成的砖头更具健壮性。就是所谓分派
2. 尽可能降低耦合度,相互之间联系少了,就不会修改时相互影响,就是所谓的封装,要封装得好,不要有一根线漏在外面,结果,一拉这根线,盒子全打开了,散架了,这怎么叫封装呢?

3. 捕获异常很重要,但是对于是不是throw,要看具体情况,throw抛多了,显得很乱,抛少了,外界会被欺骗,这是适度问题。

对于查询一条数据记录如user,如果没有就抛出一个特定额UserNotFoundException,如果记录重复,就抛出特定的UserIsExsitedException,大家觉得这样好不好?

对于查询一条数据记录如user,如果没有就抛出一个特定额UserNotFoundException,如果记录重复,就抛出特定的UserIsExsitedException,大家觉得这样好不好?


我认为错误没有必要做得这么细,我建议是在不同的层中,有专有的错误类别,下层的错误在向系统上层传递时,将在上一层做包装,这样一层套一层地向上传递,到需要错误处理的地方,再进行处理。

举个例子: 系统中有 DAO 一层,专门负责数据的存取操作。
这样,只需要定义 DAOException 就可以了,在 Application 的 Model(JavaBean) 中,如 UserModel, 它的 findUser method 可以这样定义:


User UserModel.findUserById(String id)throws ModelException{
UserDAO dao = DAOFactory.getUserDAO();
try{
return dao.findById(id);
}catch(DAOException daoe){
throw new ModelException(daoe);
}
return null;
}

如果有些错误是提供给用户看的,而不是给程序看的,就可能需要进一步封装,提供更友好的信息。可以试试在 Exception 中设置一些标志传递信息, 然后利用 ResourceBundle 来提供动态的信息格式。

对于查询一条数据记录如user,如果没有就抛出一个特定额UserNotFoundException,如果记录重复,就抛出特定的UserIsExsitedException,大家觉得这样好不好?


我觉得这样子好啊
不过太懒了,又得两个异常,又得判断,实在是懒得写阿
一般就catch Exception 了哈


throws ModelException
catch ModelException
换汤不换药了阿

一个应用只有一个AppException(String msg),在msg中写明错误,总觉得exception太多好像没必要。

自定义的异常有2中作用,一种是外包系统异常,一种是外包操作异常。所以2中异常是必要的,而操作异常的细化有利于调试。

我觉得首先对exception应该重新认识,异常就是我调用之前无法检查处理的意外。我希望发生的时候让我知道的。
既然是我需要知道的,当然要做一定的抽象和分类。所以对于常用的异常的分包,达到大家的共识和重用是一项非常有意义的工作。
我同意banq的说法。

When you define a new type of exception class,
you create a new type that can be used by
the application for exception recovery.
The downside is that you get one or more classes to
maintain and it gets you a long list of catch statements.

If you provide detailed exception information through
error message, this message is not "standardized".
The client code can not try to recover based on
this detailed information. Instead, this message is usually
printed out to the screen to help the end users create
exception report to support stuff and client program
logges it out for stuff to diagnose what problem is.

It all depends whether you want the client to recover.
For exception such as database is not available or a table
is not defined or system configuration file is not there,
etc., it is not recoverable, in this case, you might just
define a generic system exception.

Desig is about trade off.

Programming is art, at least some of the people day.
So there is no right or wrong. There might be a bit
better or worse, given the requirements.

For exception exception class design, same thing.
It all boils down whether you want your client code
to be able to recover based on what exception is.
If the client can do something differently based on
the exception, then it makes sense to define some sub
classes. Otherwise not. Sometimes impossible.
For instance, SQLException, if maps to the underlying
database, such as DB2 or SQL, you even have no chance to
get the same exception error message.