11.1 理解响应式编程
响应式编程是对命令式编程进行替代的一个范例。这种替代的存在是因为响应式编程解决了命令式编程的限制。通过了解这些限制,可以更好地把握响应式模式的好处。
注意:响应式编程不是银弹。不应该从这章或是其他任何对于响应式编程的讨论中,推断出命令式编程是魔鬼而响应式编程是天使。与作为一个开发人员学习的任何东西一样,响应式编程在某些地方很合适,在某些地方完全没有,应该对症下药。
如果和许多开发者一样,都是从命令式编程起步的。很自然地,现在您所写的大多数代码都是命令式的。命令式编程是非常直观的,现在的学生在他们学校的 STEM 课程中很轻松地学习它,它很强大,以至于它构成了大部分的代码,驱动着最大的企业。
这个想法很简单:您写的代码就是一行接一行的指令,按照它们的顺序一次一条地出现。一个任务被执行,程序就需要等到它执行完了,才能执行下一个任务。每一步,数据都需要完全获取到了才能被处理,因此它需要作为一个整体来处理。
这样做还行吧...直到它不得行了。当正在执行的任务被阻塞了,特别是它是一个 IO 任务,例如将数据写入到数据库或从远程服务器获取数据,那么调用该任务的线程将无法做任何事情,直到任务完成。说穿了,阻塞的线程就是一种浪费。
大多数编程语言,包括 Java,支持并发编程。在 Java 中启动另一个线程并将其发送到执行某项工作的分支上是相当容易的,而调用线程则继续执行其他工作。尽管很容易创建线程,但这些线程可能最终会阻塞自己。在管理在多线程里面的并发是很具有挑战性的。更多的线程意味着更高的复杂度。
相反,响应式编程是函数式和声明式的。响应式编程涉及描述通过该数据流的 pipeline 或 stream,而不是描述的一组按顺序执行的步骤。响应式流处理数据时只要数据是可用的就进行处理,而不是需要将数据作为一个整体进行提供。事实上,输入数据可以是无穷的(例如,一个地点的实时温度数据的恒定流)。
注意:如果您是 Java 函数式编程新手,您可能想看看由 Pierre-Yves Saumont 《Java 函数式编程》(Manning,2017), 或者 Micha Pachta的 《Grokking Functional Programming》(Manning,2021)。
应用于一个真实世界的类比就是,将命令式编程看做一个装水的气球,响应式编程看做花园里面的水管。两者都是在炎热的夏天让您的朋友惊喜并沉浸其中的方式。但是它们的执行风格是不同的:
- 一个水气球一次能携带的它的有效载荷,在撞击的那一刻浸湿了它预定的目标。然而,水球的容量是有限的,如果您想用水泡更多的人(或把同一个人淋得更湿),您唯一的选择就是增加水球的数量。
- 一根花园水龙带将其有效载荷作为一股水流从水龙头流向喷嘴。花园水龙头接的水带的容量可能是有限的,但在打水仗的过程中水是源源不断的。只要水从水龙头进入软管,就会一直通过软管然后从喷嘴喷出。同一个花园软管是很容易扩展的,您和朋友们可以玩得更尽兴。
命令式编程就类似打水仗中的水球,本质上没有什么问题,但是拿着类似响应式编程的水管的人,在可扩展性和性能方面是有优势的。