在“没有发现问题”的情况下,通过返回null
来有效地缓存执行昂贵的无状态检查代码的结果是“不良做法”吗?好处是最小的代码,没有类/代码膨胀。
此代码说明了这一点:
public static String getErrorMessage(SomeState x) {
// do some "expensive" processing with "x"
if (someProblem)
return "Some problem";
if (someOtherProblem)
return "Some other problem";
return null; // no error message means "all OK"
}
调用代码:
String message = getErrorMessage(something);
if (message != null) {
display(message);
return;
}
// proceed
这种模式避免了必须重复执行昂贵的代码两次,方法是返回null
表示“没有错误消息,因为没有错误”。并且没有额外的“低价值”类/代码。
显而易见的替代方案是A)将检查和消息创建的问题分开:
public static boolean isOK(SomeState x) {
// do some "expensive" processing with "x"
return thereIsNoProblem;
}
public static String getErrorMessage(SomeState x) {
// do some "expensive" processing with "x"
if (someProblem)
return "Some problem";
if (someOtherProblem)
return "Some other problem";
}
调用代码:
if (!isOK(something)) {
display(getErrorMessage(something)); // expensive code called a second time here
return;
}
// proceed
执行昂贵的代码一次以确定如果出现问题,再次确定 问题是,或者B)返回一个“结果”对象,它有一个布尔字段来回答“if”部分和一个字符串字段来回答“消息”部分,例如
class MyResult { // like a struct in C
boolean ok;
String message;
// accessor methods omitted
}
public static MyResult verify(SomeState x) { ...}
调用代码:
MyResult result = verify(something);
if (!result.ok) { // spare me the lecture on accessors - this is illustrative only
display(result.message);
return;
}
会造成类膨胀,并且有点笨拙恕我直言。
以这种方式“超载”返回值是“坏”吗? 它肯定比我能想到的所有替代方案都“整洁”。
答案 0 :(得分:1)
在被调用函数中报告错误/异常情况的选项数量有限:
这些都不是特别有吸引力 - 全局是坏的,我们知道,委托/回调是笨拙和冗长的,例外是缓慢的(和野兽的标记,我们都知道)。所以前两个是最常用的选项。
如果你确实从值返回函数返回错误,那么你仍然需要返回一个值。并且,对于对象返回函数,nil是最合理的返回值。
无论如何,你最终都会回来。
答案 1 :(得分:0)
我会用:
public class ValidationResult {
public final boolean isValid;
public final String errorMessage; // may be null if isValid == true
public ValidationResult(boolean isValid, String errorMessage) {
if (!isValid && errorMessage == null) {
throw new IllegalArgumentException();
}
this.isValid = isValid;
this.errorMessage = errorMessage;
}
}
然后:
public static ValidationResult validate(SomeState x) {
// blah
return new ValidationResult(isValidTest, errorMessageIfNot);
}
和
ValidationResult r = validate();
if (!r.isValid) {
display(r.errorMessage);
}
或者,您可以让它抛出一个已检查的异常。
public class ValidationException extends Exception {
// your code here
}
然后:
public static boolean validate(SomeState x) throws ValidationException {
// ...
if (isValid) {
return true;
} else {
throw new ValidationException(message):
}
}
答案 2 :(得分:0)
只要你自己使用它就可以了,但它可以以某种方式得到改善。关于为什么可以改进,让我引用Tony Hoare:
我称之为十亿美元的错误。它是1965年空引用的发明。那时,我正在设计第一个用于面向对象语言(ALGOL W)的引用的综合类型系统。我的目标是确保所有引用的使用绝对安全,并由编译器自动执行检查。但我无法抵制引入空引用的诱惑,仅仅因为它很容易实现。这导致了无数的错误,漏洞和系统崩溃,这可能在过去四十年中造成了数十亿美元的痛苦和损害。
首先,您可以使用例外:
public static String getErrorMessage(SomeState x) throws YourException {
// do some "expensive" processing with "x"
if (someProblem)
throw YourException("Some problem");
if (someOtherProblem)
throw YourException("Some other problem");
return null; // no error message means "all OK"
}
然后你可以有一个自定义对象:
class ErrorState {
boolean hasFoundError;
String message;
ErrorState() {
hasFoundError = false;
}
ErrorState(String message) {
hasFoundError = true;
this.message = message;
}
public final static ErrorState NO_ERROR = new ErrorState();
}
最后,如果潜在错误的集合是有限的,你可以使用enum
(我认为这是更好的选择):
enum ErrorState {
NO_ERROR(""),
SOME_ERROR("Some error"),
SOME_OTHER_ERROR("Some other error");
public final String message;
ErrorState(String message) { this.message = message; }
}
答案 3 :(得分:0)
不是检查isOK()然后获取错误消息,只需使用适当的消息向isOK()添加throws异常,然后使用try / catch显示错误消息。
似乎没有理由进行多层检查,然后在一个函数满足抛出时获取错误。
答案 4 :(得分:0)
您的初始解决方案非常精细,null非常适合表示“缺少”返回值。
答案 5 :(得分:0)
我决定将我的评论作出答案。我认为这是枚举的一个很好的上下文,如下所示:
public enum OperationStatus {
ERROR1 {
@Override
public String toString() {
return "err1";
}
},
ERROR2
//The same as error 1
//
//Many error types here
SUCCESS {
@Override
public String toString() {
return "ok";
}
}
}
这样,您的方法可以返回此枚举的值,并使用您可以作为String
获取的代码指定实际错误。特别是如果您计划本地化此类错误,例如,您可以切换本地化的捆绑包并获取每个已定义代码的关联值。
另一方面,如果您不打算在将来添加错误,或者如果出现新的错误类型,您可以承担此枚举所需的偶尔修改,则此方法可行。
一个替代方案(你仍然需要更新这个数组,但是你要防止自己有一个枚举)来避免枚举,一个简单的Stirng[]
也可以做到这一点,因为你可以返回一个{{ 1}}作为错误代码,并将其用作该数组的索引,以获取您可以解释,显示和本地化的实际代码。
答案 6 :(得分:0)
我非常反NULL,而且,就个人而言,将返回空字符串""
表示没有错误,或者可能是"OK"
之类的常量。但是null是可以接受的。
枚举/值对象概念确实有点像矫枉过正,但通常需求会扩展,最终你需要这样的东西。
如果可能有多个错误,我想返回一个字符串列表,一个空列表显然没有问题。在这种情况下,请 not 返回null,返回一个空列表。