zoukankan      html  css  js  c++  java
  • 异常不可用于逻辑处理

    异常不可用于逻辑处理

    异常是错误处理,但是不可以用于逻辑处理,假设我们封装了一个MsgQueue,这个类负责收集由服务端一条接受线程收集客户端消息,另一条工作线程负责取出消息,并对消息进行处理。

    class Msg {...};
    class MsgQueue {
    public:
    void put(Msg msg) throw (std::length_error);
    Msg get() throw (std::out_of_range);
    .....
    }

    这里定义了MsgQueue的两个方式,分别是收集客户端消息,然后保存消息的put方法,还有处理客户端消息,取出客户消息的get方法。

    对于put方法,由于内存有限,我们不可能让这个MsgQueue无限度的保存消息,所以当保存的消息过多,无法再保存客户端消息时,我们抛出std::length_error异常。

    对于get方法,假设queue里没有任何元素了,我们要告诉调用者queue是空的,但是这里无法返回一个空元素,所以我们的做法就是抛出一个std::out_of_range(STL的queue当queue是空时,调用front()方法居然可以取出元素,不抛出任何异常)。

    工作线程的部分代码如下:

    Msg msg;
    while (true) {
    try {
    msg = msgQueue.get(); //取出客户端消息
    handle(msg); //处理客户端消息
    } catch (std::out_of_range) {
    sleep(1);
    }
    }

    msgQueue由工作线程和工作线程共享,工作线程不断轮询,尝试msgQueue是否可以取出消息,不可以表示没有新的消息的到来,就等待一秒,可以取出消息就进行处理。

    这段代码的问题在于把异常用于逻辑处理了,msgQueue为空,这个场景是正常场景,因为不可能每个时刻都有消息过来,异常是用于错误处理的,所以合适的做法我们是应该先判断msgQueue是否为空,不为空,我们才调用msgQueue.get()取出消息。

    class MsgQueue {
    public:
    bool isEmpty();
    void put(Msg msg) throw (std::length_error);
    Msg get() throw (std::out_of_range);
    .....
    }

    添加判断queue是否为空的成员函数后,工作线程的代码修改如下:

    Msg msg;
    while (true) {
    if (msgQueue.isEmpty()) {
    sleep(1);
    continue;
    }
    msg = msgQueue.get(); //取出客户端消息
    handle(msg); //处理客户端消息
    }

    这样代码就简洁明了很多,没有像try {} catch{}导致成块代码的出现,尤其是层次一多,代码很难一目了然。

    作为MsgQueue不仅要考虑调用者各个调用场景,应该提供判断isEmpty()之类的方法,避免调用者在做逻辑处理时,不得去用异常捕获,写出一些难以理解的代码。

    当局部的控制可以满足我们的业务时,就不要使用异常处理了,请用异常做错误处理。

    何时该使用try和catch

    异常适用于哪些错误处理呢,哪里我需要try {} catch{}呢,例如网络异常,文件IO异常这些典型的错误,从一个服务器中,读取服务器的响应,可能会出现网络中断,这样的处理代码是合适使用try {} catch{}处理的。

    try {
    writeRequestToServer();
    readResponseFromServer();
    } catch (NetworkError) {
    //对网络错误进行处理,例如像浏览器说服务器无法连接
    }

    例如像文件IO,我们读写的过程中,如果真的那么倒霉,遇到硬盘坏道,也必须做处理,一个健壮的程序是要考虑任何能想到的错误的

    try {
    writeFileToDisk();
    } catch (IO_Error) {
    //假设是IO坏道,一般能做的处理就是提示用户说磁盘或者分区损坏了
    }

    这些都是错误处理,是使用异常try {} catch{}的场景,看代码人的一看就知道try{}里的代码可能会发生错误,出现错误是非法场景,但是又不能完全避免。

    总结

    try{} catch{}这些错误处理语句,能少用就少用,代码成块出现,很容易让代码变得难以阅读,如果你用异常来做逻辑处理,代码中出现try{} catch{}的概率会大大提高,导致代码可阅读性很差。

    例如当if else的代码块有三层以上

    if (...) {
    if (...) {
    if (....) {
    if (...) {
    ....
    }
    }
    }
    }

    这样的代码阅读性很差(还没有加else,否则更晕),if的层次超过了3层,我就觉得代码该重构了,因为我觉得就算是这段代码的开发者,过了一个星期后,要看看这段代码,重新梳理逻辑,至少要一个多小时,如果你重构了,你只需要十分钟就可以看懂代码逻辑了(题外话了)

    使用try {} catch {}会导致代码块的层次增加,代码可阅读性变差,公认的做法是用来做错误处理,如果用来做逻辑处理,会带来没有必要的麻烦,请尽量避免使用。

  • 相关阅读:
    JeePlus:代码结构
    JeePlus:项目部署
    JeePlus:Maven 安装配置
    JeePlus:目录
    框架-Java:JeePlus
    Java-JSP:EL表达式
    Template-FreeMarker:模板开发指南
    Template-FreeMarker:什么是 FreeMarker?
    FreeMarker:
    Template-FreeMarker:目录
  • 原文地址:https://www.cnblogs.com/ggjucheng/p/2311967.html
Copyright © 2011-2022 走看看