Java 中的回调模式:精通异步通信
大约 3 分钟
也称为
- 调用后
- 事件订阅
- 监听器
回调设计模式的意图
Java 回调设计模式是指作为参数传递给其他代码的可执行代码;预计其他代码将在合适的时间调用回(执行)该参数。
回调模式的详细解释,并附有现实世界示例
现实世界示例
在餐厅行业可以找到回调设计模式的现实世界类比。想象一下,你在一家繁忙的餐厅点餐。与其在柜台前等待食物做好,不如将你的电话号码提供给收银员。一旦你的订单准备就绪,厨房工作人员会打电话或发短信通知你,你的餐点已准备好取餐。
在这个类比中,下订单类似于启动异步任务。提供你的电话号码类似于传递回调函数。厨房准备你的订单代表异步处理,你收到的通知是回调的执行,让你无需闲置等待即可取餐。这种任务启动和任务完成的分离是回调设计模式的本质。
简单来说
回调是传递给执行程序的方法,该方法将在定义的时刻被调用。
维基百科说
在计算机编程中,回调,也称为“调用后”函数,是作为参数传递给其他代码的任何可执行代码;预计其他代码将在给定时间调用回(执行)该参数。
Java 中回调模式的编程示例
我们需要在执行的任务完成后得到通知。我们为执行程序传递一个回调方法,并等待它回调我们。
Callback
是一个具有单个方法的简单接口。
public interface Callback {
void call();
}
接下来,我们定义 Task
,它将在任务执行完成后执行回调。
public abstract class Task {
final void executeWith(Callback callback) {
execute();
Optional.ofNullable(callback).ifPresent(Callback::call);
}
public abstract void execute();
}
@Slf4j
public final class SimpleTask extends Task {
@Override
public void execute() {
LOGGER.info("Perform some important activity and after call the callback method.");
}
}
最后,以下是我们执行任务并在其完成后接收回调的方式。
public static void main(final String[] args) {
var task = new SimpleTask();
task.executeWith(() -> LOGGER.info("I'm done now."));
}
程序输出
17:12:11.680 [main] INFO com.iluwatar.callback.SimpleTask -- Perform some important activity and after call the callback method.
17:12:11.682 [main] INFO com.iluwatar.callback.App -- I'm done now.
何时在 Java 中使用回调模式
在以下情况下使用回调模式
- GUI 应用程序或事件驱动系统中的异步事件处理
- 实现通知机制,其中某些事件需要触发其他组件中的操作。
- 解耦需要交互但彼此之间没有直接依赖关系的模块或组件
Java 中回调模式的现实世界应用
- GUI 框架通常使用回调来处理事件,例如用户交互(点击、按键)
- Node.js 严重依赖回调进行非阻塞 I/O 操作
- 处理异步操作的框架,如 JavaScript 中的 Promise,使用回调来处理异步任务的解析或拒绝
- CyclicBarrier 构造函数可以接受一个回调,该回调将在每次屏障被触发时被触发。
回调模式的优点和权衡
优点
- 将操作的执行逻辑与信号或通知逻辑解耦,从而增强模块化和可重用性
- 促进异步处理,提高应用程序的响应能力和可扩展性
- 支持反应式编程模型,其中组件可以对发生的事件做出反应
权衡
- 回调地狱或金字塔:深度嵌套的回调会导致难以阅读和维护的代码
- 控制反转会导致代码流程更难跟踪,使调试更具挑战性
- 错误处理方面的潜在问题,尤其是在使用异常的语言或环境中,因为错误可能需要通过回调传播
相关的 Java 设计模式
- 命令:在回调操作需要更多灵活性或状态的情况下,回调可以实现为命令对象
- 观察者:回调可以看作是观察者模式的一种更动态、更轻量级的形式,它能够动态订阅和取消订阅回调函数
- Promise:在某些语言或框架中,可以使用 Promise 或 Future 来更简洁地处理异步操作,通常使用回调来处理成功或失败的情况