响应式编程第二弹:RxJava 2设计探索
Posted 前沿技墅
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了响应式编程第二弹:RxJava 2设计探索相关的知识,希望对你有一定的参考价值。
作者 知秋
本名李飞,长期致力于基础代码库的研发工作,通过博客与视频平台bilibili,结合自己的经验做了大量源码解读的分享。对JDK、Spring、RxJava、SpringReactor、Netty、Reactor-Netty等有很深刻的研究和独到的见解,并以此打造“Java编程方法论系列丛书”。基于开源精神,与志同道合的伙伴们一起创建了simviso开源分享团队,为开源社区服务。
近些年来,从RxJava,到Java 9中引入的响应式流API,再到Spring WebFlux,乃至MongoDB推荐使用的响应式Java客户端驱动等,响应式编程在很多场合被高频地提及。我们可以大胆猜测,响应式编程在未来一定会成为最受欢迎的编程思想实践之一。作为响应式编程的Java语言实现,RxJava封装设计理念值得细品。
源的创建设计思路
先来看一段Observable的相关代码:
从create方法可以看到,其目的就是产生和发送元素,在此对之前的内容再复习、整理一下。对于源来讲,其核心是subscribe方法的实现,也就是所谓的subscribeActual方法的实现,而源应该具备向下发送元素的能力。这里的下发动作应该属于业务范畴,按照我们在第3章最后所介绍的适配、装饰和代理来进行设计,这样就可以设计一个类首先继承Observable,得到源类型(这里是指ObservableCreate),然后设计一个具体的接口来承上启下(这里是指ObservableOnSubscribe),这也是装饰增强模式的应用之一(这种设计往往属于函数式接口应用,其“实现承上,参数启下”,参数类型应该是对外界所传参数的包装类,也就是将两个业务分开,承上的放在方法中,启下的放在参数中)。我们将后面的订阅者(即Observer)接入进来,通过ObservableOnSubscribe所实现的方法传入参数的类型,对其进行包装,达到所谓的启下。说得通俗一点就是,这个函数式接口就是一个电源转换器,目的是将Observable适配到Observer。于是就有了如下的源码设计:
// 用来进行承上启下
// 通过继承得到我们想要的服务类型
// 业务的封装
最后,人为实现发送元素,也就是我们在ObservableOnSubscribe中做的事情,既产生了元素,又发送了元素,达到了我们的预期,也留给了人为操作的接口。这种设计方式在设计底层基础库代码的时候十分有用,我们对各个部分的任务都做了明确的分工,确定所做事情的服务对象,然后进行通用的接口设计,即通过一个函数式接口对象来达到承上启下的作用(此为核心),既实现了服务对象提供的通用接口方法,也方便了下游的接入操作(此处往往涉及对下游操作的包装),对外使用者只需提供对此函数式接口的业务实现。也就是说,此函数式接口其实在此做了类似于中间件的实现,对数据元素生产与消费者的适配接入都做了很好处理。所以说,我们对RPC和消息中间件的一些灵感,又何尝不是从基础代码中获得的呢?
中间操作的转承
我们讨论了源的创建,接着讨论初始源到各个操作源之间的事情。
下面从Observable的fromArray产生元素的源来入手:
可以看到产生元素的地方就是数组array,其下发动作发生在ObservableFromArray.From-ArrayDisposable#run中,我们将目光聚集到这个run方法,发现其拿到元素就下发,并没有对数组array中的元素进行其他操作。假如元素是基本类型的话,这就是值传递,可以保证源的不可变性,但若元素是引用类型,则不一定。
接下来通过一个filter操作看看源之间的传递:
我们都知道,在未发生订阅,即没有触发subscribeActual方法之前,各种操作一直做的都是创建实例,也就是实现内部构造器的动作,这里就是ObservableFilter(Observable- Source<T> source, Predicate<?super T> predicate)构造器。而该构造器内的主要动作就是将上一个源的引用赋值给this.source:
再结合之前学过的各种操作组合,可以看到每经过一个操作就将上一个源对象和自己捆绑到一起,环环相扣。其实在各种操作最后调用subscribe方法之前,我们会包装出一个对象,其ObservableSource字段实现了类似于继承的模式,通过强引用将各个操作所设定的Observable对象给串联起来,就好像绑在一根绳上的蚂蚱。然后我们会发现,在调用subscribe方法之后,最外层操作(即最后一个操作)返回的Observable对象会首先接触到自定义的Observer,而subscribeActual里的动作就是对传入的Observer参数对象进行包装并由上一步传入的ObservableSource对象进行订阅(subscribe),从外往里对我们定义的Observer对象进行逆包装,好比通过蚂蚱绳封装了所有的业务操作,而这些业务操作就是放在靠近Observer一边的。这也体现了RxJava的设计理念之一,对元素的操作业务归根结底应该由Observer来完成,我们不应该从理念上干涉,但可以从设计上归并。由此,得出两条主线(Observable对象的创建传递主线与订阅后Observer的逆向包装传递主线)传递串联的技巧,这也是我们在代码设计中常用的手法,比如Spring框架、Netty框架等对于上下文Context的传递都有类似应用。关于业务的包装处理,更多地体现在适配上,而这个适配的封装设计在架构上就是一个中间件形式,在发布源处配置一下,在Observer处再配置一下,然后连接起来,于是对内各个操作类是高度内聚紧凑的,对外这个设计理念是高度抽象、可重用的。
小结
通过一系列的探索,相信大家心里已经比较有谱了。最后要说一下RxJava设计里面的问题,即其无法保证引用类型的状态,这需要我们注意。也就是当下发元素是引用类型时,比如这个元素是数组,那么修改数组里的元素,源数据也会被修改。这违背了源不可变性原则,所以要特别注意一定要保证下发元素的状态不可改变。但假如传递的是基本类型,因为是值传递,所以我们可以放心修改。
对于各种操作的设计执行,最后总结为一句,那就是操作组合设计是从上到下的,即所谓的套娃制作设计(先确定最外层,也就是源的实现,然后层层向内设计,产生订阅的那一刻结束设计)。而在执行的时候(产生订阅的时候),就相当于套娃的组装,先放最小的(即我们常见的LambdaObserver的封装),然后放第二小的,以此类推,直至最后放最大的,这里其实是将之前的操作进行逆向拼接(拼接的是Observer),拼接完成后接触下发元素。
本文节选自新书《Java编程方法论:响应式RxJava与代码设计实战》。响应式是未来的编程趋势,因此很有必要深入了解响应式RxJava 2的源码,从设计到实现细节,以达到RxJava 2源码库组件级别的拓展。阅读原文推波助澜,将你带入拥有大量案例的宝藏,感受按照功能迭代方式进行的讲解,饱览海量教学视频——就问你,做好迎娶白富美一举走上人生巅峰的心理建设了吗?
内容简介:本书不仅介绍RxJava2,更是通过一个成熟、优秀的代码库来向读者展示阅读源码的思路及编程方面的技巧,其中包含了并发编程的实战技巧、数据结构的设计方法、设计模式的使用方法、函数式编程的各种技巧等,希望读者可以深入思考并获得属于自己的一套编程方法论。这只是“Java编程方法论系列丛书”的第一本,后续还有对Reactor 3、Reactor-Netty、Spring WebFlux,以及JDK等方面解读的书籍,欢迎大家关注。
长按二维码,关注“前沿技墅”,抢先接收新知、了解书讯、结识大咖【期期有奖,波及面广】。任何伟大,无不起步于不经意间,你可以选择不经意错过,或不经意开始……
看完,赶紧点个“在看”鸭
点鸭点鸭
↓↓↓↓
以上是关于响应式编程第二弹:RxJava 2设计探索的主要内容,如果未能解决你的问题,请参考以下文章
重磅!好友「知秋」的新书《Java编程方法论:响应式RxJava与代码设计实战》正式预售啦