【问题标题】:Exceptions, return values, and contextual information异常、返回值和上下文信息
【发布时间】:2009-11-19 21:45:31
【问题描述】:

我知道这类问题已被反复询问,但是,对于我正在研究的问题,我还没有找到明确的答案。

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

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

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

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

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

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

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

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)知道或维护有关先前验证的状态,这是不应该的。考虑到这个类可能对几个不同的、独立的字符串进行验证,让它维护任何给定字符串的验证状态似乎是错误的。

我想这个问题本质上是在问一个人如何设计通用的方法 - 不要通过不必要地抛出异常来将法律掌握在自己手中,而是允许调用者决定严重程度 - 但仍然允许调用者获取详细的上下文信息如果呼叫者需要传达问题。有没有更好的方法来完成上述操作?

抱歉,如果这很冗长:/任何回复将不胜感激。

Ciao.

【问题讨论】:

    标签: exception-handling


    【解决方案1】:

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

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

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

    【讨论】:

      【解决方案2】:

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

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

      【讨论】:

        【解决方案3】:

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

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2012-02-19
          • 1970-01-01
          • 2014-03-15
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多