什么优雅地进行错误处理(clean code阅读笔记之六)

次来了问题怎么惩罚


流动:正文中之援是直接引用作者吧,两长长的横线中间的段子的是自自己的见解,其他大约都得以算笔记了。


错误处理是十分必要的,但是要是对不当处理使用不当则会于代码变得深叠,让阅读者看无清代码的逻辑,更要紧的凡,这也会见被程序变得杀薄弱。本文中将列出一些使错误处理的技术,帮助而勾勒来既简单而健康的代码。

使用Exception若果未是归回码

回回码是一个历史遗留问题,在以前的远非Exception的语言(比如c语言)中,它是可行且必需的,但是当出Exception的言语中使用返回码是没其余功利的。

对此下返回码的函数,调用者在获取调用结果(这里是回去回码)之后如果及时去验证返回码,这对于代码的可读性和结构的合理性都是大幅度的挑战,使用「异常处理」能给事情逻辑和错误处理在代码结构及分别,代码的布局以及逻辑会重新清晰。

首先把try-catch-finally片写出来

当遇需要开老大处理的时光,首先将try-catch-finally片写出来,这能帮助您勾勒来更好的老处理代码。

永久使unchecked异常


立即还要是一个异常有争议性的话题,到底是利用checked异常,还是unchecked异常。

_checked异常(通常是Exception的子类)是Java倍受之一个概念,它是靠如果一个艺术抛来这颇,那么它们便必为显式地捕获或者传递,通常是在术齐上加声明。

unchecked异常指的凡可能随时抛出的非常(通常是RuntimeExceptionError,和他们的子类)。

推荐阅读:Unchecked Exceptions — The
Controversy,在即时首Oracle的官指南中言语到:如果您如从某个大中恢复,那么即便应用checked异常,否则用unchecked异常,意思就是是unchecked的要命表示系统有问题了,那么要停止下来,去检查到底什么问题,然后解决。_


笔者的观是:如果以少数特殊之景象下要使捕获异常并作出处理,那么只能以checked异常。但是以平凡的开过程被应该避免使用checked异常。

checked异常的采用确带动了片补,但是呢又也致使了一部分问题,最关键的凡其违反了「开闭」原则。比如使一个计抛来某checked异常,而此特别的处理(通常是catch)是在3只章程调用层级之上,那么当你改改最底部这个法的弃来深时,所有在斯调用链中的不二法门签名都亟待给改动。这分明违背了「对修改关闭」的准,也违反了面向对象中「封装内部贯彻」的特征。

供十分的上下文

每当很中肯定要供可供应者很的捕获者(通常是于catch遭)用来甄别这深的上下文信息,使之好通过log或其他艺术最终表现出来。

比如调用者的需要定义格外类

当我们定义一个不胜类时,可以按这可怜类所属之模块,抑或是这好类的色来定义,但是当我们当先后中定义一个要命类时,更多之尽管应当是在设想「这个坏会于啊状态下叫擒获」。代码6-1受到显示了通常的以类型定义的特别类,

//代码6-1
ACMEPort port = new ACMEPort(12);
try {
    port.open();
}  catch (DeviceResponseException e) {
    reportPortError(e);logger.log("Device response exception", e);
}  catch (ATM1212UnlockedException e) {
    reportPortError(e);logger.log("Unlock exception", e);
}  catch (GMXError e) {
    reportPortError(e);logger.log("Device    response exception");
}  finally {...
}

代码6-2受显了下一个打包类定义之一个初的不胜类,不仅代码看起更加清晰简单,而且还会见带重新多外的便宜。

//代码6-2
LocalPort port = new LocalPort(12);
try {
    port.open();
}  catch (PortDeviceFailure e) {
    reportError(e);
    logger.log(e.getMessage(), e);
}  finally {
    ...
}

public class LocalPort {
    private ACMEPort innerPort;
    public LocalPort(int portNumber) {
        innerPort = new ACMEPort(portNumber);
    }
    public void open() {
        try {
            innerPort.open();
        }  catch (DeviceResponseException e) {
            throw new PortDeviceFailure(e);
        }  catch (ATM1212UnlockedException e) {
            throw new PortDeviceFailure(e);
        }  catch (GMXError e) {
            throw new PortDeviceFailure(e);
        }
    }
    ...
}

像代码6-2着那样对第三着的类库中之API进行封装会带来多补。实际上,封装第三方API可以算是一种极品实践。

当你当您的类型遭到对第三正值类库进行了温馨的包后,相当给解耦了您的项目及夫第三在类库,你可无限制之将是类库更换掉,更要紧的凡,你得无需一定论这类库的规划来行使其,比如代码6-2受所出示之,本来类库中定义了大多单重合

概念常规流程

尽管如此咱可描绘起很好的错误处理代码,它们外形优雅、结构清晰,但是有时我们连无是眷恋只要「终止代码的逻辑流程」而丢弃来大,这种情景就算绝不使用大类来处理,应该下更正常的流水线来处理,比如代码6-3惨遭非常处理。

//代码6-3
try {
    MealExpenses expenses = expenseReportDAO.getMeals(employee.getID());
    m_total += expenses.getTotal();
} catch(MealExpensesNotFound e) {
    m_total += getMealPerDiem();
}

代码6-3未遭的错误处理只是为了处理同栽独特状况(可能是数据库被寻找不至此次吃饭的花状态),而未是设暂停计算而抛开来怪,那么它们好吃重构为代码6-4受之楷模。

//代码6-4
MealExpenses expenses = expenseReportDAO.getMeals(employee.getID());
m_total += expenses.getTotal();

//这里引入一个新的特殊情况的类
public class PerDiemMealExpenses implements MealExpenses {
    public int getTotal() {
        // return the per diem default
    }
}

这种编程模式为称作破例情况模式,它以一个初的类似来拍卖这种奇特情形,那么尽管无欲抛来深类,因为以生的场面下为会见回一个MealExpenses对象,那么所有工艺流程看起便与平常的业务流程一样了。

甭回Null

Null是凶恶了,不要当代码中回到Null

设若您的代码中生出返回Null的情状,那么当代码的外地方还或抛出NullPointerException假定导致系统崩溃退出,你的代码中为恐怕会见设有一大堆的论断「一个靶是否为Null」的代码,这可能是拥有开发者都无甘于看到的状态,可以采用代码6-4被所用的特种情形模式来避免返回Null,永远回一个有值的对象。

Oracle绝不传递Null

当函数调用的参数中传递Null大凡比返回Null重复邪恶的政工。所以于您协调之代码中,永远不要传递Null

相关文章