前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >响应式编程第二弹:RxJava 2设计探索

响应式编程第二弹:RxJava 2设计探索

作者头像
用户1682855
发布2019-06-19 18:36:14
5060
发布2019-06-19 18:36:14
举报
文章被收录于专栏:前沿技墅

近些年来,从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),拼接完成后接触下发元素。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-06-18,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 前沿技墅 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
消息队列 TDMQ
消息队列 TDMQ (Tencent Distributed Message Queue)是腾讯基于 Apache Pulsar 自研的一个云原生消息中间件系列,其中包含兼容Pulsar、RabbitMQ、RocketMQ 等协议的消息队列子产品,得益于其底层计算与存储分离的架构,TDMQ 具备良好的弹性伸缩以及故障恢复能力。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档