从 Java 中的方法返回状态标志和消息的最佳方法
-
21-08-2019 - |
题
我有一个看似简单的场景,我想要一个简单的解决方案,但不清楚哪个是“最正确的”或“最Java的”。
假设我在某个类中有一个小的验证(客户端客户端)方法。身份验证可能会因多种原因而失败,我想返回一个简单的布尔值用于控制流,同时也为用户返回一条字符串消息。这些是我能想到的可能性:
- 返回一个布尔值,并传入一个 StringBuilder 来收集消息。这是最接近 C 风格的方法。
- 引发异常而不是返回 false,并包含消息。我不喜欢这样,因为失败并不罕见。
- 使用布尔值和字符串创建一个名为 AuthenticationStatus 的新类。对于一个小方法来说,这似乎有点矫枉过正。
- 将消息存储在成员变量中。这会引入潜在的竞争条件,我不喜欢它暗示某些并不真正存在的状态。
还有其他建议吗?
编辑 错过了这个选项
- 成功返回 null - 这不安全吗?
编辑 解决方案:
我选择了最面向对象的解决方案并创建了一个小型的 AuthenticationResult 类。我不会用任何其他语言这样做,但我喜欢用 Java。我也喜欢返回字符串[]的建议,因为它就像零返回但更安全。Result 类的优点之一是,如果需要,您可以获得包含更多详细信息的成功消息。
解决方案
返回一个带有布尔标志和内部字符串的小对象可能是最像 OO 的方法,尽管我同意对于像这样的简单情况来说这似乎有点过分了。
另一种选择是始终返回一个字符串,并用 null(或空字符串 - 您选择哪个)表示成功。只要 javadoc 中清楚地解释了返回值,就不应该有任何混淆。
其他提示
您可以使用异常......
try {
AuthenticateMethod();
} catch (AuthenticateError ae) {
// Display ae.getMessage() to user..
System.out.println(ae.getMessage());
//ae.printStackTrace();
}
然后如果您的 AuthenticateMethod 中发生错误,您将发送一个新的 AuthenticateError (扩展异常)
避免返回“哨兵值”,尤其是 null。您最终将得到一个代码库,其中调用者在不阅读实现的情况下无法理解方法。对于 null 的情况,如果调用者忘记(或不知道)您的方法可能返回 null,则可能会出现 NullPointerExceptions。
Bas Leijdekkers 的元组建议是一个很好的建议,如果我想从一个方法返回多个值,我会一直使用它。我们使用的是 P2<A, B>
来自 函数式Java 图书馆。这种类型是其他两种类型的联合(它包含每种类型的一个值)。
为控制流抛出异常有点代码味道,但受检查的异常是从方法获取多种类型值的一种方法。但也存在其他更清洁的可能性。
你可以有一个
Option<T>
具有两个子类的抽象类Some<T>
和None<T>
. 。这有点像 null 的类型安全替代方案,也是实现部分函数(未为某些参数定义返回值的函数)的好方法。这 函数式Java 图书馆有一个功能齐全的Option
实现的类Iterable<T>
, ,所以你可以这样做:public Option<String> authenticate(String arg) { if (success(arg)) return Option.some("Just an example"); else return Option.none(); } ... for(String s : authenticate(secret)) { privilegedMethod(); }
或者,您可以使用两种类型的不相交联合,作为
Either<L, R>
班级。它包含一个值,该值的类型为L
或者R
. 。这个类实现了Iterable<T>
对彼此而言L
和R
, ,所以你可以这样做:public Either<Fail, String> authenticate(String arg) { if (success(arg)) return Either.right("Just an example"); else return Either.left(Fail.authenticationFailure()); } ... Either<Fail, String> auth = authenticate(secret); for(String s : auth.rightProjection()) { privilegedMethod(); } for(Fail f : auth.leftProjection()) { System.out.println("FAIL"); }
所有这些课程, P2
, Option
, , 和 Either
在各种情况下都很有用。
更多选项:
- 为每种类型的失败返回一个单独的枚举值。枚举对象可以包含消息
- 返回一个 int 并有一个单独的方法从数组中查找适当的消息
- 创建一个可以包含两个值的通用实用程序元组类。这样的类可以在更多地方发挥作用。
简单的元组示例,实际实现可能需要更多:
class Tuple<L, R> {
public final L left;
public final R right;
public Tuple( L left, R right) {
this.left = left;
this.right = right;
}
}
您可以返回错误消息的集合,空表示没有问题。这是您的第三个建议的改进。
我个人认为使用布尔值和字符串创建一个名为 AuthenticationStatus 的新类是最像 Java 的方式。虽然这看起来有点矫枉过正(很可能是这样),但对我来说,它似乎更干净,更容易理解。
身份验证失败很常见,但这并不意味着它不是例外。
在我看来,身份验证失败是 典型儿童 检查异常的用例。(出色地...也许文件不存在是典型的用例,但身份验证失败是关闭#2。)
我自己使用“小类”,通常带有内部类。我不喜欢使用参数来收集消息。
另外,如果可能失败的方法是“低级别”的 - 例如来自应用程序服务器或数据库层,我更愿意返回带有返回状态的枚举,然后将其转换为 GUI 级别的字符串。如果您要将代码国际化,请不要在低级别传递用户字符串,因为这样您的应用程序服务器一次只能以一种语言进行响应,而不是让不同的客户端以不同的语言工作。
这是您有此类要求的唯一方法吗?如果没有,只需生成一个带有 isSuccessful 标志和消息字符串的通用 Response 类,并在任何地方使用它。
或者您可以让该方法返回 null 以显示成功(不太漂亮,并且不允许返回成功和消息)。
我很可能会选择类似的东西:
class SomeClass {
public int authenticate (Client client) {
//returns 0 if success otherwise one value per possible failure
}
public String getAuthenticationResultMessage (int authenticateResult) {}
//returns message associated to authenticateResult
}
通过这种“设计”,您可以仅在身份验证失败时请求消息(我希望这种情况 99,99% 的情况都会发生;))
将消息解析委托给另一个类也可能是一种很好的做法。但这取决于您的应用程序需求(主要是它需要 i18n 吗?)
这似乎是其他编程语言中的常见习惯用法,但我无法弄清楚是哪一种(我在问题中读到时猜测是 C )。
尝试从单个函数返回两个值可能会产生误导。但正如这样做的尝试所证明的那样,它也可能非常有用。
如果这是之前发布的应用程序中的常见流程,那么使用结果创建小班肯定是正确的方法。
这是关于从函数返回两个值的引用:
作为编程样式,这个想法在面向对象的编程语言中没有吸引力。返回对象表示计算结果是 这 返回多个值的习惯用法。有些人建议您不必为无关值声明类,但是也不应该从单个方法返回无关的值。
我在 java 允许的功能请求中找到了它 多个返回值
查看日期为“评估”的部分:2005-05-06 09:40:08
成功的身份验证应该是“正常”情况,因此身份验证失败是例外情况。
无论如何,用户的不同状态字符串是什么?我只能看到两个,成功或失败。任何进一步的信息都是潜在的安全问题。带有异常的解决方案的另一个优点是不会以错误的方式调用,并且失败情况更加明显。无一例外,你写:
if (authenticate()) {
// normal behaviour...
}
else {
// error case...
}
您可能会意外调用该方法而忽略返回值。然后,在没有成功验证的情况下执行“正常行为”代码:
authenticate();
// normal behaviour...
如果您使用异常,则不会发生这种情况。如果您决定不使用异常,至少命名该方法,以便清楚地表明它返回一个状态,例如。G。:
if (isAuthenticated()) {
//...
}
这里有很多很好的答案,所以我会简短地说。
我认为用户身份验证失败可以被视为检查异常的有效情况。如果您的编程风格喜欢处理异常,那么就没有理由不这样做。它还删除了“如何从方法返回多个值,我的方法做了一件事它对用户进行身份验证”
如果您要返回多个值,那么花 10 分钟创建一个通用 PairTuple(也可以是多个 TripleTuple,我不会重复上面列出的示例)并以这种方式返回您的值。我讨厌使用小型 dto 样式对象来返回各种多个值,它们只会让地方变得混乱。
返回一个字符串怎么样?为空或 Null 表示成功。失败时出现错误消息。最简单的就可以了。但不确定读起来是否好。
返回对象。如果需要,它允许您将附加功能放入类中。Java 中的短命对象可以快速创建和收集。
我会首先选择例外选项。
但是,其次,我更喜欢 C 风格的技术:
public boolean authenticate(Client client, final StringBuilder sb) {
if (sb == null)
throw new IllegalArgumentException();
if (isOK()) {
sb.append("info message");
return true;
} else {
sb.append("error message");
return false;
}
}
这并不奇怪,框架中的很多地方都这样做了。
我通常只返回一个存储所有返回信息的数组,而不是为返回类型创建一个特殊的对象。好处是您可以使用新元素扩展该数组,而无需创建新类型和混乱。缺点是您必须确切地知道当从特定方法返回数组时应显示哪些元素才能正确解析它。通常我同意某些结构,例如第一个元素始终是表示成功的布尔值,第二个元素是带有描述的字符串,其余的都是可选的。例子:
public static void main(String[] args)
{
Object[] result = methodReturningStatus();
if(!(Boolean)result[0])
System.out.println("Method return: "+ result[1]);
}
static Object[] methodReturningStatus()
{
Object[] result = new Object[2];
result[0] = false;
result[1] = "Error happened";
return result;
}