例外,返回值和上下文信息

时间:2009-11-19 21:45:31

标签: exception-handling

我知道这类问题一再被问到,但是,我还没有找到一个明确的答案来解决我正在研究的问题。

从我所阅读的有关异常处理的所有内容看来,一般的共识是异常只应用于特殊情况。我还在许多地方读过,在可能的情况下,应该使用返回值来指示问题或失败(例如登录失败,某种验证失败)。我的问题是,当使用这些返回值时,如何传达问题的上下文信息?除了例外,可以将上下文信息添加到异常中并允许它冒泡。让我尝试使用代码示例来解释:

假设我们有一个基本的抽象类(我遗漏了一些细节),它代表了String的某种格式定义。这个类基本上决定了给定字符串的格式应该如何。

public abstract class ADataEntryDefinition
{
    public boolean isValid(String data);
}

让我说我扩展它以对字符串执行一些安全验证:

public class SecureDataEntryDefinition extends ADataEntryDefinition
{
    public boolean isValid(String data)
    {
        //do some security checks on the format of the data
    }        
}

validate方法将接受一个String,如果该字符串与该类定义的数据定义匹配,则返回true。

继续,假设我有一个管理其中几个数据定义的类,这个类的职责是验证逗号分隔的String中的每个条目与它维护的一个数据定义。

public class DataSetDefinitions
{
    private List<ADataEntryDefinition> dataDefinitions = ...

    public boolean isValid(String dataValues)
    {
        //obtain each string in dataValues delimited by a ',' into String[]
        //called dataEntryValues

        int i=0;
        for (ADataEntryDefinition dataEntry : dataDefinitions)
        {
            if (!dataEntry.isValid(dataEntryValues[i++])
            {
                return false;
            }
        }
        return true;           
    }        
}

现在,对我来说,这些方法似乎可以在无效数据的情况下抛出异常(例如,在某些情况下可能会出现无效数据)。在这种情况下,我喜欢返回true / false以指示验证失败并随后允许调用者判断其严重程度的方法。因此呼叫者执行以下操作:

boolean success = false;
success = dataSetDefinitions.isValid(someString);

假设像上面这样的特定调用者认为失败的验证是关键的,因此,必须随后抛出异常以防止继续处理;它应该在何处获取传达问题所需的上下文信息...如何知道由于 SecureDataEntryDefinition 类(或任何其他类)中的安全问题,验证中的2层(调用)实际上失败了这个问题的子类。)

我想我可以添加一个这样的方法:

public class DataSetDefinitions
{
    private List<ADataEntryDefinition> dataDefinitions = ...

    public boolean isValid(String dataValues)
    {
        ....
    }

    public String getValidationErrorMsg() {...}
}

将返回上次失败验证的错误消息。然后,在验证失败时,调用者可以完成以下操作:

success = dataSetDefinitions.isValid(someString);
if (!success)
    throw new SomeException(dataSetDefinitions.getValidationErrorMsg());

但是对我而言,这似乎让类(在这种情况下是 DataSetDefinitions )知道或保持关于先前验证的状态,它不应该。考虑到这个类可以执行几个不同的独立字符串的验证,它似乎是错误的,因为它保持关于任何给定字符串验证的状态。

我想这个问题本质上是问一个人如何设计一般的方法 - 不要通过不必要的方式抛出异常而让法律掌握,但允许呼叫者决定严重程度 - 但仍允许呼叫者获取详细的上下文信息如果呼叫者需要传达问题。有没有更好的方法来做到这一点?

如果这是非常啰嗦道歉:/任何回复都将受到赞赏。

3 个答案:

答案 0 :(得分:1)

您可以返回用户定义的类而不是简单的bool,以便提供更多上下文信息。

这与事件使用的策略类似。我们有一个EventArgs类,其他类派生这个类,以便为给定类型的事件提供更多的上下文信息。

答案 1 :(得分:1)

不要归还布尔。返回一个封装成功/失败状态的类,以及相关信息。这样,您可以执行以下操作:

DataEntryStatus status = isValid(...);

if (!status.isValid()) {
   throw status.generateStatusException();
}

并且状态对象本身生成相应的异常,从而保持封装。

答案 2 :(得分:0)

我大部分时间解决它的方法是定义几个类常量并返回它们。然后在我的控制器的业务逻辑中,我将静态地检查这些值。

<?php
class Test
{
   const SUCCESS = 1000;
   const EMAIL_FAIL = 2001;
   const SAVE_FAIL  = 2002;

   ...

   public function save($value)
   {
      if (!$this->writetodb($value)
         return self::SAVE_FAIL;
      elseif(!$this->sendMailToAdmin())
         return self::EMAIL_FAIL;
      else
         return self::SUCCESS;
   }
}




$test = new Test();
$result = $test->save('my value');
switch ($result) {
   case Test::SUCCESS:
      echo 'Yay!';
      break;
   case Test::SAVE_FAIL:
      echo 'Error saving!';
      break;
   case Test::EMAIL_FAIL:
      echo 'Error sending email!';
      break;
}