我知道这类问题一再被问到,但是,我还没有找到一个明确的答案来解决我正在研究的问题。
从我所阅读的有关异常处理的所有内容看来,一般的共识是异常只应用于特殊情况。我还在许多地方读过,在可能的情况下,应该使用返回值来指示问题或失败(例如登录失败,某种验证失败)。我的问题是,当使用这些返回值时,如何传达问题的上下文信息?除了例外,可以将上下文信息添加到异常中并允许它冒泡。让我尝试使用代码示例来解释:
假设我们有一个基本的抽象类(我遗漏了一些细节),它代表了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 )知道或保持关于先前验证的状态,它不应该。考虑到这个类可以执行几个不同的独立字符串的验证,它似乎是错误的,因为它保持关于任何给定字符串验证的状态。
我想这个问题本质上是问一个人如何设计一般的方法 - 不要通过不必要的方式抛出异常而让法律掌握,但允许呼叫者决定严重程度 - 但仍允许呼叫者获取详细的上下文信息如果呼叫者需要传达问题。有没有更好的方法来做到这一点?
如果这是非常啰嗦道歉:/任何回复都将受到赞赏。
侨
答案 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;
}