函数式响应式编程-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),是灯泡监听开关的状态,随着开关状态的改变,而采取自己的行动,这里,灯泡就是响应式的了

函数式响应式编程-FRP简介(一)

    代码描述如下(以下的代码是观察者模式的实现方式,真正的响应式编程并不是这样的):


    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简介的主要内容,如果未能解决你的问题,请参考以下文章

FRP-Functional Reactive Programming-函数响应式编程

函数响应式编程(FRP)思想-Callback风格

IOS函数响应式编程开发简析

热点关于响应式编程你可能错过的信息

在Python中探索函数式响应型编程(FRP)

函数式 响应编程