函数式响应式编程-FRP简介
Posted AI一大数据
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了函数式响应式编程-FRP简介相关的知识,希望对你有一定的参考价值。
函数式响应式编程,简称FRP,也叫函数式反应型编程,也可以称函数式反应式编程,总之一些中国特有的文字游戏,只需记住FRP即可。 F,即functional,函数;R,即reactive,反应、响应;P,即programming,编程之意。
FRP最近几年在技术领域很火,但是它到底是什么,你为什么需要关注它,它背后的原理是什么,估计很少人能说清楚,那么现在,我们一起一探究竟。
反应式(响应式)编程
首先,我们先看一下反应式(响应式)编程,注意,不是函数式编程,也不是函数式反应型编程,这是三个不同的概念。
我们先从一个简单的例子着手:一个开关和一个灯泡,开关控制灯泡,在编码期间,这两个组件被关联起来,一般来说,我们很少仔细去想怎么关联这两个组件,现在我们来仔细想下这个问题。
一种处理方式我们称之为主动方式(proactive),是通过开关来改变灯泡的状态,此时,开关是主动的,灯泡是被动的,灯泡只是简单的接收开关的指令。
如下图
代码描述如下:
public class Switch {
LightBulb lightBulb;
void onFlip ( boolean enabled) {
lightBulb.power(enabled);
}
}
这里,开关类里包含了一个灯泡的实例,灯泡的状态总是能够随着开关的指令而改变。
另外一种处理方法我们称之为响应方式(reactive),是灯泡监听开关的状态,随着开关状态的改变,而采取自己的行动,这里,灯泡就是响应式的了
代码描述如下(以下的代码是观察者模式的实现方式,真正的响应式编程并不是这样的):
public interface LightBulbIF {
public void power(boolean bol);
}
public class LightBulb implements LightBulbIF {
public void power(boolean bol) {
System.out.println("I am ON ? " + bol);
}
}
public static LightBulb create (Switch theSwitch) {
LightBulbIF lightBulb = new LightBulb();
theSwitch.addOnFlipListener(enabled -> lightBulb.power(enabled));
return lightBulb;
}
Switch代码可如下编写:
public class Switch {
List<LightBulbIF> listeners = new ArrayList<LightBulbIF>();
public void notify() {
for (LightBulbIF listener : listeners) {
listener.power(true);
}
}
public LightBulbIF addOnFlipListener(LightBulbIF lightBulb) {
listeners.add(lightBulb);
return lightBulb;
}
}
public static void main(String[] args) {
Switch sw = new Switch();
LightBulbIF lightBulb1 = T01.create(sw);
LightBulbIF lightBulb2 = T01.create(sw);
LightBulbIF lightBulb3 = T01.create(sw);
sw.notify();
}
在这个响应式的编码方式里,灯泡观察开关发出的事件,从而改变自己的状态。
对于终端用户来说,无论是主动方式还是响应方式,达到的效果是一样的,那么他们到底有什么不同呢?
第一个不同是: 谁控制灯泡的亮灭,主动方式里,灯泡(LightBulb)必须由它的外部组件即开关(Switch)调用它的power方法来控制自己的亮灭,而响应方式里,自己控制自己的亮灭。
第二个不同是:谁决定开关控制什么,主动方式里,开关自己决定它控制什么(蓝灯泡、绿灯泡,全由开关控制),而响应方式里,它只负责注册自己的监听者。
尽管你可以根据不同的场景,选择主动方式或者响应方式编程,但是主动方式编程里,Switch直接控制LightBulb,耦合度很高,而响应式则不同,类之间不互相直接控制,而是借助listener关联其他类,这是一种松耦合的编码方式。
在上面那个响应式的例子里,也存在一些问题:
首先,监听者不通用,就是说Switch.OnFlipListener这个添加监听者的方法只适合Switch这个类,每个被监听的对象都需要实现自己的注册监听者的方法。
其次,监听者直接进入被监听者内部,LightBulb必须在Switch类中,才能监听到Switch的变化,这样又会导致紧耦合,与我们的目标相去甚远,如下面的代码,监听者类型必须是LightBulbIF,这是重点,真正的响应式编程,就是要完全解耦:
public class Switch {
List<LightBulbIF> listeners = new ArrayList<LightBulbIF>();
public void notify() {
for (LightBulbIF listener : listeners) {
listener.power(true);
}
}
看看上面这段代码,你会发现:
1. 状态的变化(即事件)会按照listener注册的顺序即刻通知给各个listener,而listener则直接执行自己的操作,设想这样一个场景,用户在搜索输入框内输入关键词,一般情况下每次输入框的内容变化都会产生一个事件,触发一个搜索操作,如果用户输入了一个错字,并且在一秒之内修改了一下,那么就会触发三次搜索操作:第一次按错误的关键字搜索,第二次按照删除了错字的关键字搜索,第三次按照正确的关键字搜索。如果这是一个同时在线人数很多的平台,这样的操作会给系统带来毫无意义的负担,而真正的响应式编程会通过scheduling events(后续探讨)来避免这种情况。
2. 你无法保证在notify的时候,所有的listener都已经注册了。也就是说,注册listener的代码与触发事件的代码是分开的,复杂环境下,两者的顺序靠代码书写是无法保证的,而真正的响应式编程具有事务性,可以避免发生这种情况。
还有listener实现线程安全的情况,忘记注销listener的情况等等,这些问题都可以通过FRP编程解决,后续篇章我们一起学习。
首先我们知道了FRP要解决的问题,以后再学习FRP相关的概念时,就能够更又针对性的理解,敬请扫码关注盲点技术号,我们将持续努力与您一起学习探讨开发技术
以上是关于函数式响应式编程-FRP简介的主要内容,如果未能解决你的问题,请参考以下文章