理解响应式编程

响应式的由来

我们先来聊一聊响应式的由来,对于它的由来,我们可能需要先从一段常见的代码片段看起

int a=1;
int b=a+1;
System.out.print(“b=”+b)    //  b=2
a=10;
System.out.print(“b=”+b)    //  b=2

上面是一段很常见的代码,简单的赋值打印语句,但是这种代码有一个缺陷,那就是如果我们想表达的并不是一个赋值动作,而是b和a之间的关系,即无论a如何变化,b永远比a大1。那么可以想见,我们就需要花额外的精力去构建和维护一个b和a的关系。

而响应式编程的想法正是企图用某种操作符帮助你构建这种关系。

它的思想完全可以用下面的代码片段来表达:


int a=1;
int b <= a+1;   // <= 符号只是表示a和b之间关系的操作符
System.out.print(“b=”+b)    //  b=2
a=10;
System.out.print(“b=”+b)    //  b=11

这就是是响应式的思想,它希望有某种方式能够构建关系,而不是执行某种赋值命令。

至此你可能不禁要问,我们为什么需要构建关系的代码而不是命令式的代码呢?如果你翻一翻自己正在开发的APP,你就能看到的每一个交互的页面其实内部都包含了一系列的业务逻辑。而产品的每个需求,其实也对应了一系列的业务逻辑相互作用。总之,我们的开发就是在构建一系列的业务逻辑之间的关系。你说我们是不是需要构建关系的代码?

说回响应式,前期由于真实的编程环境中并没有构建关系的操作符,主流的编程语言并不支持这种构建关系的方式,所以一开始响应式主要停留在想的层面,直到出现了Rx和一些其他支持这种思想的框架,才真正把响应式编程引入到了实际的代码开发中。

Rx是响应式拓展,即支持响应式编程的一种拓展,为响应式在不同语言中的实现提供指导思想


什么是响应式编程

说完了了响应式的由来,我们就可以谈谈什么是响应式编程了。

响应式编程是一种通过异步和数据流来构建事物关系的编程模型。这里每个词都很重要,“事物的关系”是响应式编程的核心理念,“数据流”和“异步”是实现这个核心理念的关键。为了帮助大家理解这个概念,我们不妨以APP初始化业务为例来拆解一下这几个词。

这是一个比较理想化的APP初始化逻辑,完成SDK初始化,数据库初始化,登陆,之后跳转主界面

事物的关系

  • 事物

    • 是一个十分宽泛的概念,它可以是一个变量,一个对象,一段代码,一段业务逻辑.....但实际上我们往往把事物理解成一段业务逻辑(下文你均可以将事物替换为业务逻辑来理解),比如上图中,事物就是指APP初始化中的四个业务逻辑。
  • 事物的关系
    • 这种关系不是类的依赖关系,而是业务之间实际的关系。比如APP初始化中,SDK初始化,数据库初始化,登陆接口,他们共同被跳转页面业务所依赖。但是他们三个本身并没有关联。这也只是业务之间较为简单的关系,实际上,根据我们的需求App端会产生出许多业务之间错综复杂的关系。

数据流

关于Rx的数据流有很多说法,比如“Everything is a stream”,“Thinking with stream”等等。虽然我明白这只是想强调流的重要性,可是这些话折射出来的编程思路其实是很虚无缥缈的,只会让开发者对于Rx编程更加迷惑。

实际上,数据流只是事物之间沟通的桥梁。

比如在APP初始化中,SDK初始化,数据库初始化,登陆接口这些业务完成之后才会去安排页面跳转的操作,那么这些上游的业务在自己工作完成之后,就需要通知下游,通知下游的方式有很多种,其中最棒的的方式就是通过数据(事件)流。每一个业务完成后,都会有一条数据(一个事件)流向下游,下游的业务收到这条数据(这个事件),才会开始自己的工作。

但是,只有数据流是不能完全正确的构建出事物之间的关系的。我们依然需要异步编程。

异步

异步编程本身是有很多优点的,比如挖掘多核心CPU的能力,提高效率,降低延迟和阻塞等等。但实际上,异步编程也给我们构建事物的关系提供了帮助。

在APP初始化中,我们能发现SDK初始化,数据库初始化,登陆接口这三个业务本身相互独立,应当在不同的线程环境中执行,以保证他们不会相互阻塞。而假如没有异步编程,我们可能只能在一个线程中顺序调用这三个相对耗时较多的业务,最终再去做页面跳转,这样做不仅没有忠实反映业务本来的关系,而且会让你的程序“反应”更慢

小结

总的来说,异步和数据流都是为了正确的构建事物的关系而存在的。只不过,异步是为了区分出无关的事物,而数据流(事件流)是为了联系起有关的事物

APP初始化应该怎么写

许多使用Rx编程的同学可能会使用这种方式来完成APP的初始化。

Observable.just(context)
            .map((context)->{login(getUserId(context))})
            .map((context)->{initSDK(context)})
            .map((context)->{initDatabase(context)})
            .subscribeOn(Schedulers.newThread())
            .subscribe((context)->{startActivity()})

其实,这种写法并不是响应式的,本质上还是创建一个子线程,然后顺序调用代码最后跳转页面。这种代码依然没有忠实反映业务之间的关系。

在我心目中,响应式的代码应该是这样的


Observable obserInitSDK=Observable.create((context)->{initSDK(context)}).subscribeOn(Schedulers.newThread())

Observable obserInitDB=Observable.create((context)->{initDatabase(context)}).subscribeOn(Schedulers.newThread())

Observable obserLogin=Observable.create((context)->{login(getUserId(context))})
                              .map((isLogin)->{returnContext()})
                            .subscribeOn(Schedulers.newThread())

Observable observable = Observable.merge(obserInitSDK,obserInitDB,obserLogin)

observable.subscribe(()->{startActivity()})

大家应该能很明显看到两段代码的区别,第二段代码完全遵照了业务之间客观存在的关系,可以说代码和业务关系是完全对应的。

那么这带来了什么好处呢?当initSDK,initDB,Login都是耗时较长的操作时,遵照业务关系编写响应式代码可以极大的提高程序的执行效率,降低阻塞。

理论上讲,遵照业务关系运行的代码在执行效率上是最优的。

为什么引入响应式编程

对响应式编程有了一些了解之后,我知道马上会由很多人跳出来说,不使用这些响应式编程我们还不是一样开发APP?

在这里我希望你理解一点,当我们用老办法开发APP的时候,其实做了很多妥协,比如上面的APP初始化业务,三个无关耗时操作为了方便,我们往往就放在一个线程环境中去执行,从而牺牲了程序运行的效率。而且实际开发中,这种类似的业务逻辑还有很多,甚至更加复杂。假如不引入响应式的思路,不使用Rx的编程模型,我们面对这么些复杂的业务关系真的会很糟心。假如你做一些妥协,那就会牺牲程序的效率,假如你千辛万苦构建出业务关系,最终写出来的代码也一定很复杂难以维护。所以,响应式编程其实是一种更友好更高效的开发方式。

根据个人经验来看,响应式编程至少有如下好处:

  • 在业务层面实现代码逻辑分离,方便后期维护和拓展
  • 极大提高程序响应速度,充分发掘CPU的能力
  • 帮助开发者提高代码的抽象能力和充分理解业务逻辑
  • Rx丰富的操作符会帮助我们极大的简化代码逻辑

一个复杂一些的例子

接下来,我就以我们团队目前的一款产品的页面为例,详细点介绍运用响应式编程的正确姿势。

首先,UI和产品沟通后,可能会给我们这样的设计图(加上一些尺寸的标注)。但是我们并不需要急忙编码,我们首先要做的是区分其中相对独立的模块。

上图我做了一点简单的标注。把这个页面的业务逻辑简单的分为四个相互独立的模块,分别是视频模块,在线人数模块,礼物模块,消息模块。他们相互独立,互不影响。接下来,我们再去分析每个模块内部的业务并构建起业务之间的关系。大致如下:

构建了业务之间的关系图,其实我们的工作已经完成了一半了,接下来就是用代码实现这个关系图。在这里,我就以其中一小段业务关系来编写代码给大家示范。

    Observable obserDownload=Observable.just(url)
                                        .map((url)->{getZipFileFromRemote(url)});
    Observable obserLocal=Observable.just(url)
                                        .map((url)->{getZipFileFromLocal(url)});
    Observable obserGift=Observable.concat(obserLocal,obserDownload)
                                        .takeUnitl((file)->{file!=null});
    obserGift.subscribeOn(Schedulers.io()).flatMap((file)->{readBitmapsFromZipFile(file)})
                                        .subscribe((bitmap)->{showBitmap(bitmap)})

以上是我手写的伪代码,可能细节上有些问题,但大体思路就是这样。

有人可能会说,那是因为你运用操作符比较熟练才能这么写。其实操作符都是我查的,我记不住那么多操作符,所以基本上我都是先理清楚业务之间的关系,需要和并逻辑的时候,就去去查合并类的操作符,需要条件判断来分流的逻辑时去找条件判断类的操作符。基本上都能满足需求。你瞧,写代码就是这么简单,后续即使需要增加需求,代码修改起来也很清晰,因为无关的业务已经被你分离好了。

所以,赶紧在你的项目中引入响应式编程吧!

作者:拉丁吴
链接:https://www.jianshu.com/p/c95e29854cb1
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

作者:拉丁吴
链接:https://www.jianshu.com/p/c95e29854cb1
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

原文地址:https://www.cnblogs.com/DjangoBlogs/p/9229000.html

时间: 2024-11-10 00:10:05

理解响应式编程的相关文章

rxjs学习(一)响应式编程理解

响应式编程理解 响应式编程是为了解决异步的问题,异步的问题是指因为回调导致的代码难以维护的问题,一般在非常多异步的项目中 这种问题会恶化 我们来思考一下异步的产生,异步是因为一个对象与另外一个对象交互,因为需要等待,所以增加回调函数处理等待结果,所以我们可以想象一下 假如一个系统中有10个对象,然后这些对象之间都会互相交互,而且还存在顺序的问题,当然最终代码肯定是可以写出来的,不过很难以维护,这不仅是回调的问题,加入a对象与b对象交互,那么我们把a对象和b对象用线连起来,表示他们之间的一段业务联

响应式编程系列(一):什么是响应式编程?reactor入门

响应式编程 系列文章目录 (一)什么是响应式编程?reactor入门 (二)Flux入门学习:流的概念,特性和基本操作 (三)Flux深入学习:流的高级特性和进阶用法 (四)reactor-core响应式api如何测试和调试? (五)Spring reactive: Spring WebFlux的使用 (六)Spring reactive: webClient的使用 引言 Spring framework 5 的一大新特性:响应式编程(Reactive Programming).那么什么是响应式

Unity基于响应式编程(Reactive programming)入门

系列目录 [Unity3D基础]让物体动起来①--基于UGUI的鼠标点击移动 [Unity3D基础]让物体动起来②--UGUI鼠标点击逐帧移动 时光煮雨 Unity3D让物体动起来③—UGUI DoTween&Unity Native2D实现 时光煮雨 Unity3D实现2D人物动画① UGUI&Native2D序列帧动画 时光煮雨 Unity3D实现2D人物动画② Unity2D 动画系统&资源效率 背景 前有慕容小匹夫的一篇<解构C#游戏框架uFrame兼谈游戏架构设计&

RAC响应式编程

RAC响应式编程开源地址:https://github.com/ReactiveCocoa/ReactiveCocoa# 作者 cbsfly_iDev 2016.01.04 21:06* 写了13984字,被134人关注,获得了134个喜欢 学习RAC小记-适合给新手看的RAC用法总结 字数1855 阅读4287 评论4 喜欢33 最近叶大直播写代码,我也做点小笔记. 什么是RAC? 几乎每一篇介绍RAC的文章开头都是这么一个问题.我这篇文章是写给新手(包括我自己)看的,所以这个问题更是无法忽视

什么是函数响应式编程(Java&amp;Android版本)

什么是函数响应式编程(Java&Android版本) 原文链接:http://www.bignerdranch.com/blog/what-is-functional-reactive-programming/ 函数响应式编程(FRP)为解决现代编程问题提供了全新的视角.一旦理解它,可以极大地简化你的项目,特别是处理嵌套回调的异步事件,复杂的列表过滤和变换,或者时间相关问题. 我将尽量跳过对函数响应式编程学院式的解释(网络上已经有很多),并重点从实用的角度帮你理解什么是函数响应式编程,以及工作中

IOS响应式编程框架ReactiveCocoa(RAC)使用示例

ReactiveCocoa是响应式编程(FRP)在IOS中的一个实现框架,它的开源地址为:https://github.com/ReactiveCocoa/ReactiveCocoa# :在网上看了几篇文章,感觉理论讲了很多,但是代码还是看不太懂,于是自己把它github文档上的一些使用的经典示例实现了一下,项目中有需要时可以直接搬过去用,用的熟练了再读源码也比较容易理解. 例1. 监听对象的成员变量变化,当成员变量值被改变时,触发做一些事情. 这种情况其实就是IOS KVO机制使用的场景,使用

使用ReactiveCocoa实现iOS平台响应式编程

使用ReactiveCocoa实现iOS平台响应式编程 ReactiveCocoa和响应式编程 在说ReactiveCocoa之前,先要介绍一下FRP(Functional Reactive Programming,响应式编程),在维基百科中有这样一个例子介绍: 在命令式编程环境中,a = b + c 表示将表达式的结果赋给a,而之后改变b或c的值不会影响a.但在响应式编程中,a的值会随着b或c的更新而更新. Excel就是响应式编程的一个例子.单元格可以包含字面值或类似"=B1+C1″的公式,

函数响应式编程(FRP)思想

序 ReactiveCocoa是IOS广为使用的技术框架,而ReactiveCocoa的核心思想就FRP.FRP不同于JAVA的object-oriented和AOP,FRP能让你的代码像数学一样简洁,业务像流水一样清晰流畅. 函数响应式编程 响应式编程思想为体,函数式编程思想为用. 响应式编程 例如,在命令式编程环境中, a:=b+c表示将表达式的结果赋给 a,而之后改变 b 或 c的值不会影响 a.但在响应式编程中,a的值会随着 b或 c的更新而更新. 在响应式编程当中,a:=b+c声明的是

RxJava响应式编程之初级了解

据说现在流行的开发模式是 Retrofit+RxJava+MVP+ButterKnife 如果想要简单学习ButterKnife.MVP模式,可以参考我以前的例子 使用butterknife注解框架 Android-MVP设计模式高级(三) 今天我就简单来学习下RxJava的相关知识 以前我也只是听说过RxJava,RxJava这个到底是什么东西呢? 呵呵,它其实是一个库,所以我们使用里面的方法,得需要下载库,所以我们需要在AS中进行配置 1.RxJava 地址以及添加 github地址: ht