浅谈响应式编程(Reactive Programming)

这是告别CSDN后第一次使用简书写IT类的博客,还在适应。最不适应的就是不能直接手输markdown语法标记。(好像原因是我没有切换编辑器)

什么是响应式编程(Reactive Programming)

In computing, reactive programming is an asynchronous programming paradigm concerned with data streams and the propagation of change. This means that it becomes possible to express static (e.g. arrays) or dynamic (e.g. event emitters) data streams with ease via the employed programming language(s), and that an inferred dependency within the associated execution model exists, which facilitates the automatic propagation of the change involved with data flow.

-- Wikipedia

以上解释来自维基百科,在计算机领域,响应式编程是一个专注于数据流和变化传递的异步编程范式。这意味着可以使用编程语言很容易地表示静态(例如数组)或动态(例如事件发射器)数据流,并且在关联的执行模型中,存在着可推断的依赖关系,这个关系的存在有利于自动传播与数据流有关的更改。

抛开大段大段的概念,我们先搞清楚一件事情:什么是编程范式?

通俗的说:编程是为了解决问题,而解决问题可以有多种视角和思路,其中具有普适性的模式被归结为范式。我们常说的:“面向对象”,“面向过程”都是编程范式。

响应式编程是一种从数据流和变化出发的解决问题的模式。所以要研究响应式编程,一定要牢记已经掌握的OO(面向对象,笔者妄断大家OO的思想都是很根深蒂固了)来做对比,也一定要抛开OO避免钻牛角尖

为什么是异步?

在展开这个问题前,我们先看一个故事,引自知乎:小故事

摘抄如下:

老张爱喝茶,废话不说,煮开水。

出场人物:老张,水壶两把(普通水壶,简称水壶;会响的水壶,简称响水壶)。

1 老张把水壶放到火上,立等水开。(同步阻塞)
老张觉得自己有点傻

2 老张把水壶放到火上,去客厅看电视,时不时去厨房看看水开没有。(同步非阻塞)
老张还是觉得自己有点傻,于是变高端了,买了把会响笛的那种水壶。水开之后,能大声发出嘀~~~~的噪音。

3 老张把响水壶放到火上,立等水开。(异步阻塞)
老张觉得这样傻等意义不大

4 老张把响水壶放到火上,去客厅看电视,水壶响之前不再去看它了,响了再去拿壶。(异步非阻塞)
老张觉得自己聪明了。

所谓同步异步,只是对于水壶而言。普通水壶,同步;响水壶,异步。虽然都能干活,但响水壶可以在自己完工之后,提示老张水开了。这是普通水壶所不能及的。同步只能让调用者去轮询自己(情况2中),造成老张效率的低下。

所谓阻塞非阻塞,仅仅对于老张而言。立等的老张,阻塞;看电视的老张,非阻塞。情况1和情况3中老张就是阻塞的,媳妇喊他都不知道。虽然3中响水壶是异步的,可对于立等的老张没有太大的意义。所以一般异步是配合非阻塞使用的,这样才能发挥异步的效用。

上面这个小故事还是有点问题,但基本可以说明问题了。

响应,一定是对一个事件、一个信号(诸如此类的描述)产生了反应。响水壶的响应是什么呢?水温达到一定程度,水壶的反应是会响。水壶响了,声音传递给老张,老张的反应是去关水壶。

再看普通水壶,水温达到一定程度,水壶没有反应,水的反应是冒气泡,冒水雾。只是这个信号不太容易传递,要跑过来看,所以老张只能以轮训的方式来办事情,没法跑到一边等通知。

对于两个水壶而言,烧水都是阻塞的,水没烧完就干不了其他的事情(比如说拿来砸胡桃???)

ok,回到我们的问题:为什么是异步?

回归到本质回答这个问题:响应式编程,本质上是对数据流或某种变化所作出的反应,但是这个变化什么时候发生是未知的,所以他是一种基于异步、回调的方式在处理问题。

怪圈:似乎绝大多数博客说着说着就开始讲解RxAndroid

正如副标题,在网上搜索到的绝大多数的博客都会说着说着就在教你如何使用RxAndroid。各位,请记住以下几点:

  • RxAndroid(或RxJava)是很优秀的响应式编程框架。

  • 你并非一定需要使用RxAndroid。

  • RxAndroid并不像那些博客里面说的那样会让你的代码变得更可读。

这里我直接进入第三点。取用扔物线推荐RxJava中的例子:如下这段代码:

Observable.from(folders)
.flatMap((Func1) (folder) -> { Observable.from(file.listFiles()) }) 
.filter((Func1) (file) -> { file.getName().endsWith(".png") }) 
.map((Func1) (file) -> { getBitmapFromFile(file) })
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) 
.subscribe((Action1) (bitmap) -> { imageCollectorView.addImage(bitmap) });

就这段代码,是否需要从上到下仔细阅读一遍之后才能才会知道他的意图?

甚至,为了精读代码,他可能是这样:

Observable.from(folders)
    .flatMap(new Func1<File, Observable<File>>() {
        @Override
        public Observable<File> call(File file) {
            return Observable.from(file.listFiles());
        }
    })
    .filter(new Func1<File, Boolean>() {
        @Override
        public Boolean call(File file) {
            return file.getName().endsWith(".png");
        }
    })
    .map(new Func1<File, Bitmap>() {
        @Override
        public Bitmap call(File file) {
            return getBitmapFromFile(file);
        }
    })
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(new Action1<Bitmap>() {
        @Override
        public void call(Bitmap bitmap) {
            imageCollectorView.addImage(bitmap);
        }
    });

ok,请允许我再问一个问题,如此简介的代码,您打算单独用一个类来放吗?

如果对于任何一个处理类似业务逻辑的rx代码段都使用类来放,可能类数量会爆炸,而且这些类的命名看起来会很奇葩。若不这样,您的业务实现类中将充斥诸如此类不精读不敢确定语义、容易被误修改、不容易测试的代码。面对这样的代码的时候只会是如履薄冰战战兢兢

我是在反对使用RxAndroid吗?

No,我只是反对滥用Rx,我赞成对某些高度抽象的异步行为使用Rx构建具有语义性的框架代码,例如:编写MVVM分层框架。反对对任何业务细节都去做“一切皆流”的无脑工作。毕竟:业务是需要逐渐迭代发展的,对于有测试代码支撑的、同时有较强语义性的类,我们泛读代码就可以“闻弦歌而知雅意”,对于需要重构何处代码,修改何处逻辑心中有数,而不必将“流”再反转回“实际的相互关系”,再打乱,修改,再组织成流,再恶心下一次迭代,而且,最关键的是“你可能要从很多的流中找出这一个流”。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,711评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,079评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,194评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,089评论 1 286
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,197评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,306评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,338评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,119评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,541评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,846评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,014评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,694评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,322评论 3 318
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,026评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,257评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,863评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,895评论 2 351

推荐阅读更多精彩内容

  • 一.非阻塞和异步 借用知乎用户严肃的回答在此总结下,同步和异步是针对消息通信机制,同步代表一个client发出一个...
    Daniel_adu阅读 1,819评论 0 8
  • 摘自廖雪峰教程 在IO编程一节中,我们已经知道,CPU的速度远远快于磁盘、网络等IO。在一个线程中,CPU执行代码...
    rick_z阅读 383评论 0 1
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,914评论 25 707
  • 自打考上研究生就不停有学弟学妹通过各种途径问我一些考研的问题。问的人多了,有了一点有趣的发现。重点大学的小孩儿问的...
    笨鸟你要飞阅读 394评论 0 0
  • 客厅里的电视机正在播放电影《超人》,大概讲的是,一个外太空来的孩子掉到了地球,他生来就用大力,四五岁时便可轻易地举...
    林泉儿阅读 472评论 0 0