Exception handling 异常、返回值和上下文信息

Exception handling 异常、返回值和上下文信息,exception-handling,Exception Handling,我知道这类问题已经被问了一次又一次,然而,我还没有找到一个明确的答案,我正在研究的问题 从我读到的所有关于异常处理的内容来看,似乎普遍的共识是,异常只应用于异常情况。我在很多地方也读过,如果可能的话,应该使用返回值来指示问题或失败(例如登录失败、某种验证失败)。我的问题是,当使用这些返回值时,如何传达问题的上下文信息?对于异常,可以将上下文信息添加到异常中,并允许其冒泡。让我试着用一个代码示例来解释: 假设我们有一个基本的抽象类(我省略了一些细节),它表示字符串的某种格式定义。这个类本质上规定了

我知道这类问题已经被问了一次又一次,然而,我还没有找到一个明确的答案,我正在研究的问题

从我读到的所有关于异常处理的内容来看,似乎普遍的共识是,异常只应用于异常情况。我在很多地方也读过,如果可能的话,应该使用返回值来指示问题或失败(例如登录失败、某种验证失败)。我的问题是,当使用这些返回值时,如何传达问题的上下文信息?对于异常,可以将上下文信息添加到异常中,并允许其冒泡。让我试着用一个代码示例来解释:

假设我们有一个基本的抽象类(我省略了一些细节),它表示字符串的某种格式定义。这个类本质上规定了给定字符串的格式

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方法将接收一个字符串,如果该字符串与类定义的数据定义匹配,则返回true

接下来,假设我有一个类,该类管理多个数据定义,该类的职责是根据它维护的其中一个数据定义验证逗号分隔字符串中的每个条目

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;           
    }        
}
假设像上面这样的特定调用方认为失败的验证是关键的,因此必须随后抛出异常以阻止处理继续进行;它应该从哪里获得传达问题所需的上下文信息。。。它如何知道,由于SecureDataEntryDefinition类(或任何其他相关子类)中的安全问题,关闭验证的两层(调用)实际上失败了

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

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

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

    public String getValidationErrorMsg() {...}
}
但对我来说,这就像是让类(在本例中是DataSetDefinitions)知道或维护以前验证的状态,而它不应该这样做。考虑到这个类可能会对几个不同的、独立的字符串执行验证,让它维护其中任何一个字符串的验证状态似乎是错误的

我猜这个问题本质上是问一个人如何设计一种通用的方法——不通过不必要地抛出例外而将法律掌握在自己手中,但允许调用者决定严重性——但仍然允许调用者在调用者需要沟通问题时获得详细的上下文信息。有没有更好的方法来实现上述目标

道歉,如果这是非常冗长的:/任何回应将不胜感激


再见。

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


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

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

DataEntryStatus status = isValid(...);

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

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

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

<?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;
}

DataEntryStatus status = isValid(...);

if (!status.isValid()) {
   throw status.generateStatusException();
}
<?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;
}