简化函数调用之十五 :Replace Exception with Test(以测试取代异常)

面对一个「调用者可预先加以检查」的条件,你抛出了一个异常。

修改调用者,使它在调用函数之前先做检查。

double getValueForPeriod (int periodNumber) {

    try {

        return _values[periodNumber];

    } catch (ArrayIndexOutOfBoundsException e) {

        return 0;

    }

}

 

double getValueForPeriod (int periodNumber) {

if (periodNumber >= _values.length) return 0;

    return _values[periodNumber];

}

动机(Motivation)

异常(exception)的出现是程序语言的一大进步。运用Replace Error Code with Exception,异常便可协助我们避免很多复杂的错误处理逻辑。但是,就像许多好东西一样,异常也会被滥用,从而变得不再让人偷快(就连味道极好的Aventinus 啤酒,喝得太多也会让我厌烦[Jackson])。「异常」只应该被用于异常 的、罕见的行为,也就是那些「产生意料外的错误」的行为,而不应该成为「条件 检查」的替代品。如果你可以合理期望调用者在调用函数之前先检査某个条件,那么你就应该提供一个测试,而调用者应该使用它。

作法(Mechanics)

·在函数调用点之前,放置一个测试句,将函数内的catch 区段中的代码拷贝到测试句的适当if 分支中。

·在catch 区段起始处加入一个assertion,确保catch 区段绝对不会被执行。

·编译,测试。

·移除所有catch 区段,然后将区段内的代码拷贝到try 之外,然后移除try 区段。

·编译,测试,

范例:(Example)

下面的例子中,我以一个ResourcePool 对象管理「创建代价高昂、可复用」的资源(例如数据库连接,database connection)。这个对象带有两个「池」(pools), 一个用以保存可用资源,一个用以保存已分配资源。当用户索求一份资源时,ResourcePool 对象从「可用资源池』中取出一份资源交出,并将这份资源转移到 「已分配资源池」。当用户释放一份资源时,ResourcePool 对象就将该资源从「已 分配资源池」放回「可用资源池」。如果「可用资源池」不能满足用户的索求,ResourcePool 对象就创建一份新资源。

资源供应函数可能如下所示:

class ResourcePool

  Resource getResource() {

      Resource result;

      try {

          result = (Resource) _available.pop();

          _allocated.push(result);

          return result;

      } catch (EmptyStackException e) {

          result = new Resource();

          _allocated.push(result);

          return result;

      }

}

Stack _available;

Stack _allocated;

在这里,「可用资源用尽」并不是一种意料外的事件,因此我不该使用异常 (exceptions)表示这种情况。

为了去掉这里的异常,我首先必须添加一个适当的提前测试,并在其中处理「可用 资源池为空」的情况:

  Resource getResource() {

      Resource result;

      if (_available.isEmpty()) {

         result = new Resource();

         _allocated.push(result);

         return result;

     }

      else {

          try {

             result = (Resource) _available.pop();

             _allocated.push(result);

             return result;

          } catch (EmptyStackException e) {

             result = new Resource();

             _allocated.push(result);

             return result;

          }

      }

   }

现在getResource() 应该绝对不会抛出异常了。我可以添加assertion 保证这一点:

  Resource getResource() {

      Resource result;

      if (_available.isEmpty()) {

          result = new Resource();

          _allocated.push(result);

          return result;

      }

      else {

          try {

              result = (Resource) _available.pop();

              _allocated.push(result);

              return result;

          } catch (EmptyStackException e) {

            Assert.shouldNeverReachHere("available was empty on pop");

             result = new Resource();

             _allocated.push(result);

             return result;

          }

      }

  }

class Assert...

  static void shouldNeverReachHere(String message) {

      throw new RuntimeException (message);

  }

编译并测试。如果一切运转正常,就可以将try 区段中的代码拷贝到try 区段之外,然后将区段全部移除:

  Resource getResource() {

      Resource result;

      if (_available.isEmpty()) {

          result = new Resource();

          _allocated.push(result);

          return result;

      }

      else {

          result = (Resource) _available.pop();

       _allocated.push(result);

       return result;

    }

  }

在这之后我常常发现,我可以对条件代码(conditional code)进行整理。本例之中我可以使用Consolidate Duplicate Conditional Fragments:

  Resource getResource() {

      Resource result;

      if (_available.isEmpty())

          result = new Resource();

      else

          result = (Resource) _available.pop();

      _allocated.push(result);

      return result;

  }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值