Reactive programming

在上一篇中我们完成了 StickyNote 的 UI 跟 Model 的部分,後面的章节将有很大的一部分会用到 Reactive programming 的概念,在这里先做个简单介绍。

从现在开始我会使用下面几个缩写:

  • FP: Functional Programming
  • OOP: Object Oriented Programming
  • RP: Reactive Programming

Programming paradigm

我们很常会看到在介绍 RP 或是 RxJava 的人会说 RP 使用起来比较简洁,但是以我这几年的学习经验来说,RP 并没有让程序码变得更简洁乾净,相反的,在错误的使用情况下,反而会让程序码更加的混乱。依我的看法, 如果想要了解 RP、FP 跟 OOP 他们的差别的话,应该要去了解他们的切入点或是解决问题思考的方式,通常我们称这个叫 Programming paradigm 。

在写 RP 时,你可以想像你在一个水管的世界,在上游的水管中倒水进去的话,下游的水管出口在之後就会看到有水跑出来,而这些水就是我们所要处理的事件。只要管线是在连结的状态,不管你在哪个时间点倒水进去,倒多少水进去,你都可以期待这些水都会在之後被下游接收,而且会依照顺序的流出来,为了更好解释 RP 的概念,接下来我将会用水分子的方式来解说。

在 RP 中,我们会期待不只一个水分子会流过水管,因为只有一个水分子流过去的话,就有点大材小用,我们花了很多时间建造这些水管,结果只使用一次,这不是很可惜吗?所以我们期待的会是会有很多的水分子流过水管,在这过程中有可能会发生停只倒水,也可能经过生锈的水管而让流出来的水是黄色的,也有可能中间的水管太细所以新的水无法继续灌进去。水管的源头也可能不只一个,流出去的地方也不只一个。

如果说 RP 是一个水管世界的话,那 FP 我就会觉得是一个数学的世界,在数学的世界里所有函式都是可预期(Deterministic)的,有输入也有输出,同样的输入将会得到同样的输出(Pure function), 一个输入的参数你也不会预期他会突然改变本身的状态(immutable),因此处理档案读写或是网路连线状态就不是 FP 非常擅长的事情,需要额外做一些的处理,在 FP 中使用他们(Monad)的学习成本也相对的高。

那 OOP 呢?OOP 对我来说,他的思考方式是专业分工,不同的角色有不同的职责,也可以将任务交给别人完成,但你不需要知道怎麽完成,以及任务的细节。对於同样的任务来说 A、B、C都可以完成,但他们完成的方式跟结果可能会完全不一样(设计模式)。但是在传递任务的过程要小心,接收方有可能乱改任务内容,或是出一个大包,结果连累整个任务爆炸,那这个问题是怎麽产生出来的呢?不知道,因为在设计不好的情况下,每个人都有权限修改任务内容,所以也不知道是谁修改的。

  • Reactive programming: 事件流的设计方式,不适合只有单一事件的问题。
  • Functional programming: 数学以及函式的世界,适合用来写核心的商业逻辑。
  • Object oriented programming: 适合用来定义程序的架构以及各角色之间的交互关系。

RP ,FP, OOP 是可以共同存在的,在进行专案开发时不需要去严格限制全部都要使用某一种 Programming paradiam,建议大家依据需求、要解决的问题去选择适合你的开发方式。

Reactive Programming

基本上 RP 有两个最主要的角色,ObservableObserverObservable 是事件发送方,Observer 是事件接收方。除此之外,这两个角色需要产生连结,产生连结的这个动作叫做 bind ,或是以 RxJava 来说,这个动作就是 subscribe ,如下图所示。

Screen Shot 2021-08-25 at 8.56.34 PM.png

产生连结之後,任何从Observable 发出的事件,跟这个Observable 有连结的 Observer 就会接收到这些事件,如同上图所示,每个小圆点各代表一个事件,除此之外,Observable 也可以分发事件给不同的Observable ,或是汇整多个Observable 的事件成一个单一个Observable 。前者这个行为我们称作 Multicasting,後者的这个行为则称为 Merge。

Screen Shot 2021-08-25 at 9.06.28 PM.png

讲到这边可能还是有点抽象,这边用手势事件来做解说吧!手势操作的事件可以简单地分成三类: Finger Down, Finger Up, Finger Move, 分别为手指触控萤幕、手指离开萤幕、手指移动,我们可以将一系列的事件做成一张图表,如下图:

Screen Shot 2021-08-25 at 9.19.21 PM.png

上方图表所描述的事情是,一开始发出了一个 Finger Down 事件,接着连续发出了四个 Finger Move 事件,最後以一个 Finger Up 来结束。所以这是代表着什麽呢?代表使用者可能想要拖曳一个物件,从 A 点移到 B 点,所以我们可以简化使用者的意图,用下面这张图来诠释:

Screen Shot 2021-08-25 at 9.26.04 PM.png

Finger Up 跟 Finger Down 事件被省略了,因为对於只要关注“拖曳”这件事的接收方来说,他们只需要知道位移量就好,什麽时候把手指放开的这个资讯对现在来说一点都不重要。接着再看下面这个范例:

Screen Shot 2021-08-25 at 9.30.36 PM.png

依上图来看,手指在萤幕上点击了三次,前面有单独的一次,以及後面快速点击两次,所以依使用者的意图,应该可以转变成下面这样子:

Screen Shot 2021-08-25 at 9.35.37 PM.png

但是使用者的意图真的是这样子吗?还是使用者就是想要藉由双重点击来呼叫出选单呢?如下图:

Screen Shot 2021-08-25 at 9.40.05 PM.png

所以这些事件可以从原始的、比较单纯的手势事件,藉由分析意图,转化为更高阶、更抽象的概念,有了这些转换过後的事件,关注这些事件的接收方,就可以不用管 Finger Up 或是 Finger Down 这种接近底层行为的细节,直接使用这些高阶概念来应用。以下图来做示范:

Screen Shot 2021-08-25 at 9.54.56 PM.png

如果我们把时间轴拉长,可以看到更多转换过後的事件,以上图来说,依序为 Tap, Double Tap, Drag, Drag, Drag, Tap。这些事件在不同的使用情境中可以发挥不同的效果,例如第三个时间轴的使用情境可能是双击打开选单,第四个时间轴的使用情境是选择照片或是选择商品。注意到了吗?这每个时间轴都可以是一个Observable ,而第二个时间轴就是从第一个时间轴转换过来的,也就是说从第一个Observable ,做一些计算,可以产生出第二个更好用的Observable ,接着第三跟第四个时间轴则是从第二个 Observable 分发出去,也就是之前所提到的 Multicasting 这个概念。

小结

今天做了一个 RP 的简单介绍,相信相关的教学在网路上可以找到一大堆,不知道大家比较喜欢看到怎样的教学方式呢?直接看程序码呢?还是像本文一样从观念出发?还是在开发过程中有遇到什麽问题呢?欢迎大家在底下留言跟我分享你的看法吧!


<<:  DAY10 - DFS

>>:  Rust 语言和你 SAY HELLO!!

day26 老板我赶时间,给我最快完成的料理 select

提醒,select仍是实验中的api,请斟酌使用 在这之前的26天,我们所用的都是我要做什麽事,就是...

Day14 - 解析看板文章及显示

该来处理搜寻结果了。 在前几天的内容中,当我完成搜寻时,都会使用parseBoardArticle方...

2021 — 找工作 (下)

下面这边要来分享我面试有到最後一轮的公司~ SmartNews 这是一间日本的新创公司,目前算是日本...

Day.8 备份还原 - 备份资料 (MYSQL binlog )-上

复习: 在昨天讲解了如何使用mysqldump备份是数据和binlog相关过滤方法,今天来实际模拟...

[前端暴龙机,Vue2.x 进化 Vue3 ] Day12.事件处理

Vue 里面的事件处理,使用的方式会跟一般 JavaScript 的用法相近,下面跟着一起看吧~ 事...