有三个(惯用的)来处理运行时可能失败的函数。
1。返回布尔值
第一个是返回boolean。这种风格广泛用于 C 和 C 风格的 API,如 PHP。这会导致 - 例如在 PHP 中 - 经常编写这样的代码:
if (xyz_parse($data) === FALSE)
$error = xyz_last_error();
这样做的缺点是显而易见的,这就是它越来越过时的原因,尤其是在 OOP 语言和具有异常处理的语言中。
2。使用中介/状态/结果对象
如果你不使用异常,你仍然有机会在 OOP 语言中返回描述状态的对象。
例如,如果您收到来自用户的输入并想要验证输入,那么您事先知道您可能会得到垃圾,并且结果经常不会验证,你可能会写这样的代码:
ValidationResult result = parser.validate(data);
if (result.isValid())
// business logic
else
error = result.getvalidationError();
3。使用例外
Java 执行此操作,正如您在套接字中所展示的那样。原因是创建连接应该成功并且仅在需要特殊处理的异常情况下失败。
应用它
在您的情况下,我会直接抛出异常,甚至删除辅助方法。您的辅助方法命名错误。它实际上并不查询服务是否启动(顾名思义),而只是连接。
使用(检查)异常
让我们假设连接是您的方法实际上所做的。在这种情况下,我们将其命名为connectToService 更为贴切,并使其直接抛出异常:
public void connectToService() thows IOException {
// yada yada some init stuff, whatever
socket.connect();
}
public void proceedWhenError() {
try {
connectToService();
} else {
logger.debug("Exception happened, but it's alright.", exception)
// do stuff
}
}
public void doNotProceedWhenError() throws IllegalStateException {
try {
connectToService();
// do stuff
}
catch(IOException e) {
throw new IllegalStateException("Oh, we cannot proceed. The service is not up.", exception)
}
}
使用boolean
另一方面,您的serviceUp(仍然命名错误,最好称为isServiceUp)实际上查询是否服务是否正在运行(可能已由其他人启动)。在这种情况下,使用布尔值是正确的方法
public boolean isServiceUp {
return ...; // query the service however you need
}
当然,这指的是使用布尔值的第一个解决方案,当您还需要知道为什么服务未启动时,它并没有真正的帮助。
使用中介/结果/状态对象
因此,让我们摒弃这种有限的方法并使用中介/结果/状态对象对其进行改造:
class ServiceState {
enum State { CONNECTED, WAITING, CLOSED; }
State state;
IOException exception;
String additionalMessage;
public ServiceState (State state, IOException ex, String msg) {
[...] // ommitted for brevity
}
// getters ommitted for brevity
}
辅助函数将变为getServiceState:
public ServiceState getServiceState() {
// query the service state however you need & return
}
public void proceedWhenError() {
ServiceState state = getServiceState();
if (state.getState() == State.CONNECTED) {
// do stuff
} else {
logger.debug("Exception happened, but it's alright.", state.getException())
// do stuff
}
}
public void doNotProceedWhenError() {
ServiceState state = getServiceState();
if (state.getState() == State.CONNECTED) {
// do stuff
} else {
// do stuff
throw new IllegalStateException("Oh, we cannot proceed. The service is not up.", exception)
}
}
请注意,您也在proceedWhenError 中重复自己,整个代码可以简化为:
public void proceedWhenError() {
ServiceState state = getServiceState();
if (state.getState() != State.CONNECTED) {
logger.debug("Exception happened, but it's alright.", state.getException())
}
// do stuff
}
何时使用第二种情况以及何时使用第三种情况存在一些争议。有些人认为异常应该是异常的,并且您不应该在设计时考虑到异常的可能性,并且几乎总是使用第二种选择。没事儿。但是我们已经检查了 Java 中的异常,所以我认为没有理由不使用它们。我使用检查的异常
当基本假设是调用应该成功(如使用套接字),但有可能失败时,当非常不清楚调用是否应该成功(如验证数据)时,我使用第二个选项。但对此众说纷纭。
这也取决于你的辅助方法实际上做了什么。 isServiceUp 暗示它查询某些状态,但不会改变它。在这种情况下,返回一个状态对象显然是处理这种情况的惯用方式。
但是您的实现显示辅助方法连接到服务。在这种情况下,至少在 java 中,处理它的惯用方法是抛出(检查的)异常 - 但您仍然可以使用结果对象来证明是合理的。
不过,不建议只返回boolean,因为它太有限了。如果所有您需要知道是否服务是否正在运行(并且对原因不感兴趣),这样的方法可能仍然不过很有用(并且可以在幕后实现为仅执行 return getServiceState() == State.SUCCESS 的辅助方法)。
使用异常来控制工作流是一种反模式
那么,让我们看看,什么是异常?
定义:异常是在程序执行期间发生的一个事件,它扰乱了程序指令的正常流程。
来源:https://docs.oracle.com/javase/tutorial/essential/exceptions/definition.html
异常的定义是它是一个扰乱(从而改变)程序工作流程的事件。如果按照定义使用它是一种反模式,那么整个语言特性本身就必须被认为是一种反模式。有些人确实相信异常是不好的,并且一些有一些有效的论据,但一般来说,异常被认为是有用的构造并被视为处理异常工作流程的有用工具。
抛出异常当然不是反模式。 返回它,另一方面,是。因此,是的,您的代码 - 如所示 - 是非惯用的。