首页 > 编程知识 正文

一个java类对回调怎么写_Java如何实现一个回调地狱(Callback Hell)?

时间:2023-05-06 14:06:58 阅读:186362 作者:4297

关于回调地狱(Callback hell ),你不知道吧。 尤其是对于前端朋友,当然前端朋友通过Promise等各种方式避免回调地狱。 但是,我在后端的朋友中听说了很多关于回调地狱的事情,尤其是在反应编程框架(如RxJava和Reactor )兴起之后,却不多见。

为了更好地知道回调地狱Callback hell的问题在哪里,我们需要先学习如何写回调地狱。 在那之前,你必须知道什么是回调函数。

本文包括以下内容。

什么是回调

回调的优点

什么是回调地狱

为什么会发生回调地狱

回调和Future的区别是什么

如何解决回调地狱

今天我们从一开始就谈谈什么是回调函数。

什么是回调函数?

百度百科上这样说:

回调函数是从函数指针调用的函数。 如果将函数的指针(地址)作为参数传递给另一个函数,当用于调用该指针指向的函数时,就会说这是回调函数。 回调函数不是直接从函数的实现方调用的,而是在发生特定事件或条件时从另一方调用的,用于响应该事件或条件。 回调是由将方法作为第一个参数的其他方法调用的方法。 在许多情况下,回调是在发生特定事件时调用的方法。

是什么? 很难理解吗? 确实很难理解,这个说明里有指针云,对java用户真的很不友好。

作为参考,举个例子吧。 也欢迎批评和指摘。

回叫:呼叫方呼叫被叫方后,被叫方将结果反馈给呼叫方。 ) a调用b,b完成后,将结果反馈给a )举例来说,上司给员工安排工作,员工完成它。 员工完成工作后,向上司反馈工作结果。 这个过程称为回调。

这样应该很容易理解了吧。 Talk is cheap,Show me the code! 那么,用这个写简单的例子吧。

回调示例

呼叫接口

首先,写下以下Callback接口: 接口只包含一个用于Callback操作的方法。

//*

* @author振动的月饼

*/

公共接口调用{

//*

*具体实施

* @param t

*/

公共void callback (TT;

() ) ) ) )。

Boss类

因为上司是被反馈的对象,所以对于需要实现Callback这个接口并重载Callback方法的上司具体做什么,当然是做大生意,有makeBigDeals方法他需要员工。 我们重建方法向他添加员工Worker,稍后实现Worker类。

publicclassbossimplementscallback {

私有工作器工作器;

public boss (工作器工作器) {

this.worker=worker;

() ) ) ) )。

@Override

公共void callback (strings ) {

() ) ) ) )。

publicvoidmakebigdeals (finalstringsomedetail )。

worker.work(somedetail;

() ) ) ) )。

() ) ) ) )。

工作器类

员工类很简单。 出入一项工作,完成就行了。 把结果还给我就行了。 但是,如何完成回调呢?

公共类工作器{

公共字符串工作(字符串工作)。

返回'结果';

() ) ) ) )。

() ) ) ) )。

虽然我们在这种思路上很容易被认为非常符合思维逻辑,但在回调中,我们需要做一些改变。

调用代码

对员工来说,有两点是必须知道的。 谁是上司,需要做什么? 因此,输入上司和工作内容这两个参数。 具体内容分两个阶段,先完成任务,然后向上司报告。

公共类工作器{

公共语音工作(callback boss,String someWork )。

String result=someWork 'is done!' ; //作出具体处理

BOSS.callback(result ); //把结果反馈给上司

() ) ) ) )。

() ) ) ) )。

接下来,完成Boss类。 callback方法接收发送的结果并处理结果。 这里只需要打印。 在makeBigDeals方法中,上司分配工作,员工完成。 如果完成进程是异步的,则为异步调用;如果完成进程是同步的,则为同步回调。 这里采用的是异步方式。

在新线程上,运行Worker.Work(Boss.thi )

s, someDetail),其中Boss.this即为当前对象,在这里,我们正式完成了回调。

public class Boss implements Callback {

……

@Override

public void callback(String result) { // 参数为worker输出的结果

logger.info("Boss got: {}", result) // 接到完成的结果,并做处理,在这里我们仅打印出来

}

public void makeBigDeals(final String someDetail) {

logger.info("分配工作");

new Thread(() -> worker.work(Boss.this, someDetail)); // 异步完成任务

logger.info("分配完成");

logger.info("老板下班。。");

}

}

回调结果

Show me the result! 好,跑一下代码试一下。

Worker worker = new Worker();

Boss boss = new Boss(worker); // 给老板指派员工

boss.makeBigDeals("coding"); // 老板有一个代码要写

结果如下。在结果中可以看到,老板在分配完工作后就下班了,在下班后,另一个线程通知老板收到反馈"coding is done"。至此,我们完成了异步回调整个过程。

INFO 2019 九月 20 11:30:54,780 [main] - 分配工作

INFO 2019 九月 20 11:30:54,784 [main] - 分配完成

INFO 2019 九月 20 11:30:54,784 [main] - 老板下班。。

INFO 2019 九月 20 11:30:54,787 [Thread-0] - Boss got: coding is done!

我将代码示例传至Github,供大家参考。 callback代码示例

回调的优势

解耦,回调将子过程从主过程中解耦。 对于相同的输入,可能对其有不同的处理方式。在回调函数,我们完成主流程(例如上面的Boss类),对于过程中的子流程(例如上面的Worker类)从主流程中分离出来。对于主流程,我们只关心子过程的输入和输出,输入在上面的例子中即为Worker.work中的参数,而子过程的输出则是主过程的callback方法的参数。

异步回调不会阻塞主线程。上面的例子清晰可以看到,员工没有完成工作之前老板就已经下班,当工作完成后,会通过另一个线程通知老板。老板在这个过程无需等待子过程。

回调地狱

总体设计

我们将上述功能扩展,老板先将工作交给产品经理进行设计;设计完成后,交给程序员完成编码。流程示意如图。

将任务交给产品经理

首先,写一个Callback,内部new一个产品经理的的Worker,在makeBigDeal方法实现主任务,将任务交给产品经理;在重载的callback方法中,获取产品经理的输出。

new Callback() {

private Worker productManager = new Worker();

@Override

public void callback(String s) {

System.out.println("产品经理 output: " + s); // 获取产品经理的输出

}

public void makeBigDeals(String bigDeal) {

System.out.println("Boss将任务交给产品");

new Thread(() -> {

this.productManager.work(this, bigDeal); // 异步调用产品经理处理过程

}).start();

}

}.makeBigDeals("design");

再将产品经理输出交给开发

在拿到产品经理的输出之后,再将输出交给开发。于是我们在再次实现一个Callback接口。同样的,在Callback中,new一个开发的Worker,在coding方法中,调用Worker进行开发;在重载的callback方法中,获取开发处理后的结果。

@Override

public void callback(String s) {

System.out.println("产品经理 output: " + s); // 产品经理的输出

String midResult = s + " coding";

System.out.println("产品经理设计完成,再将任务交给开发");

new Callback() {

private Worker coder = new Worker();

@Override

public void callback(String s) {

System.out.println("result: " + s); // 获取开发后的结果

}

public void coding(String coding) {

new Thread(() -> coder.work(this, coding)).start(); // 调用开发的Worker进行开发

}

}.coding(midResult); // 将产品经理的输出交给开发

}

完整的实现

new Callback() {

private Worker productManager = new Worker();

@Override

public void apply(String s) {

System.out.println("产品经理 output: " + s);

String midResult = s + " coding";

System.out.println("产品经理设计完成,再将任务交给开发");

new Callback() {

private Worker coder = new Worker();

@Override

public void apply(String s) {

System.out.println("result: " + s);

}

public void coding(String coding) {

new Thread(() -> coder.work(this, coding)).start();

}

}.coding(midResult);

}

public void makeBigDeals(String bigDeal) {

System.out.println("Boss将任务交给产品");

new Thread(() -> this.productManager.work(this, bigDeal)).start();

}

}.makeBigDeals("design");

好了,一个简单的回调地狱完成了。Show me the result!

Boss将任务交给产品

产品经理 output: design is done!

产品经理设计完成,再将任务交给开发

result: design is done! coding is done!

回调地狱带来了什么?

到底什么是回调地狱?简单的说,回调地狱就是Callback里面又套了一个Callback,但是如果嵌套层数过多,仿佛掉入地狱,于是有了回调地狱的说法。

优势: 回调地狱给我们带来什么?事实上,回调的代码如同管道一样,接收输入,并将处理后的内容输出至下一步。而回调地狱,则是多个管道连接,形成的一个流程,而各个子流程(管道)相互独立。前端的朋友可能会更熟悉一些,例如Promise.then().then().then(),则是多个处理管道形成的流程。

劣势: 回调的方法虽然将子过程解耦,但是回调代码的可读性降低、复杂性大大增加。

Callback Hell示例:Callback Hell

和Future对比

在上面,我们提到异步回调不会阻塞主线程,那么使用Future也不会阻塞,和异步回调的差别在哪?

我们写一个使用Future来异步调用的示例:

logger.info("分配工作...");

CompletableFuture future = CompletableFuture.supplyAsync(() -> worker.work(someDetail));

logger.info("分配完工作。");

logger.info("老板下班回家了。。。");

logger.info("boss got the feedback from worker: {}", future.get());

在上面的代码,我们可以看到,虽然Worker工作是异步的,但是老板获取工作的结果(future.get())的时候却需要等待,而这个等待的过程是阻塞的。这是回调和Future一个显著的区别。

如何解决

如何解决回调地狱的问题,最常用的就是反应式编程RxJava和Reactor,还有Kotlin的Coroutine协程,OpenJDK搞的Project Loom。其中各有优势,按下不表。

总结

总结一下:

什么是回调。回调是调用方在调用被调方后,被调方还将结果反馈给调用方。(A调用B,B完成后,将结果反馈给A)

回调的优势。1)子过程和主过程解耦。2)异步调用并且不会阻塞主线程。

回调地狱是什么。回调地狱是回调函数多层嵌套,多到看不清=。=

为什么会出现回调地狱。每一个回调像一个管道,接受输出,处理后将结果输出到下一管道。各个管道处理过程独立,多个管道组成整个处理过程。

回调和Future有什么区别。1)两者机制不同;2)Future在等待结果时会阻塞,而回调不会阻塞。

如何解决回调地狱。最常见的则是反应式编程RxJava和Reactor。

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。