引发异常时尝试不同方法的模式

时间:2020-03-06 14:39:30  来源:igfitidea点击:

这是一个暴露我缺乏经验的问题:我有一个方法DoSomething(),如果它不能做到干净整洁,则会抛出异常。如果失败,我会尝试几次不太准确的方法DoSomethingroxly(),以期找到足够好的解决方案。如果仍然失败,我最终将调用DoSomethingInaccurateButGuaranteedToWork()。这三个都是属于该对象的方法。

两个问题:首先,这种(公认的丑陋)模式是可以接受的,还是有一种更优雅的方式?

其次,鉴于它可能会引发异常,因此最好的方法来跟踪我调用了DoSomethingroxly()的次数?我目前在对象中保留变量iNoOfAttempts,并嵌套try块...这太可怕了,我感到羞愧。

解决方案

返回错误代码,而不是引发异常。

如果这些方法失败的方式确实引发了异常,请在同一方法中将它们全部捕获并采取适当的措施,例如增加计数器并返回错误代码。

bool result = DoSomething();
   while (!result && tries < MAX_TRIES) {
       result = DoSomethingApproximately(); //This will increment tries
       if (tries > THRESHOLD) {
           result = DoSomethingThatAlwaysWorks();
       }
   }

我们绝对不应将异常用于应用程序的控制流。

在情况下,我将这三种方法组合在一起成为一个方法,并让它返回成功的特定方法,也许使用枚举或者类似方法。

怎么样(伪代码):

try{ return doSomething(); }
catch(ExpectedException) { ...not much here probably...}

for(i = 0 to RETRIES){
try{ return doSomethingApproximately; }
catch(ExpectedException) { ...not much here probably...}
}

doSomethingGuaranteed();

附录:

我强烈建议我们不要使用特殊的返回值,因为这意味着该函数的每个用户都必须知道某些返回值是特殊的。取决于函数的范围,明智的是返回可以正常处理的范围的普通部分,例如一个空集合。当然,这可能使得无法区分失败和"正确"答案是空集合(在此示例中)。

在结构路径中遍历整个函数指针。只是为了增加趣味性,我将使用一个Queue和一些LINQ。

Queue<Action> actions = new Queue<Action>(new Action[] {
   obj.DoSomething,
   obj.DoSomethingApproximately,
   obj.DoSomethingApproximately,
   obj.DoSomethingApproximately,
   obj.DoSomethingApproximately,
   obj.DoSomethingGuaranteed
});

actions.First(a => {
   try {
      a();
      return true;
   } catch (Exception) {
      return false;
   }
});