<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          什么是響應(yīng)式編程?

          共 4679字,需瀏覽 10分鐘

           ·

          2019-10-13 23:20

          本文來(lái)源:https://blog.csdn.net/get_set/article/details/79455258作者:?享學(xué)IT本文已授作者轉(zhuǎn)載權(quán)限

          1.1 什么是響應(yīng)式編程?

          在開始討論響應(yīng)式編程(Reactive Programming)之前,先來(lái)看一個(gè)我們經(jīng)常使用的一款堪稱“響應(yīng)式典范”的強(qiáng)大的生產(chǎn)力工具——電子表格。

          舉個(gè)簡(jiǎn)單的例子,某電商網(wǎng)站正在搞促銷活動(dòng),任何單品都可以參加“滿199減40”的活動(dòng),而且“滿500包郵”。吃貨小明有選擇障礙(當(dāng)然主要原因還是一個(gè)字:窮),他有個(gè)習(xí)慣,就是先在Excel上根據(jù)預(yù)算算好自己要買的東西:

          69f1e8e5b2d6f646a85a9a8f80eae069.webpimg

          相信大家都用過(guò)Excel中的公式,這是一個(gè)統(tǒng)計(jì)購(gòu)物車商品和訂單應(yīng)付金額的表格,其中涉及到一些公式:

          8782cea8591ceb37e450fad5e3058d3b.webpimg

          上圖中藍(lán)色的線是公式的引用關(guān)系,從中可以看出,“商品金額”是通過(guò)“單價(jià)x數(shù)量”得到的,“滿199減40”會(huì)判斷該商品金額是否滿199并根據(jù)情況減掉40,右側(cè)“訂單總金額”是“滿199減40”這一列的和,“郵費(fèi)”會(huì)根據(jù)訂單總金額計(jì)算,“最終應(yīng)付款”就是訂單總金額加上郵費(fèi)。

          1.1.1 變化傳遞(propagation of change)

          為什么說(shuō)電子表格軟件是“響應(yīng)式典范”呢,因?yàn)椤皢蝺r(jià)”和“數(shù)量”的任何變動(dòng),都會(huì)被引用(“監(jiān)聽”)它的單元格實(shí)時(shí)更新計(jì)算結(jié)果,如果還有圖表或數(shù)據(jù)透視圖引用了這塊數(shù)據(jù),那么也會(huì)相應(yīng)變化,做到了實(shí)時(shí)響應(yīng)。變化的時(shí)候甚至還有動(dòng)畫效果,用戶體驗(yàn)一級(jí)棒!

          這是響應(yīng)式的核心特點(diǎn)之一:變化傳遞(propagation of change)。一個(gè)單元格變化之后,會(huì)像多米諾骨牌一樣,導(dǎo)致直接和間接引用它的其他單元格均發(fā)生相應(yīng)變化。

          f3207cb8ab1f0565edeea8659951e1a2.webptitle

          看到這里,你可能會(huì)說(shuō),“切~ 不就是算付款金額嗎,購(gòu)物網(wǎng)站上都有這個(gè)最基礎(chǔ)不過(guò)的功能啊~”,這就“響應(yīng)式”啦?但凡一個(gè)與用戶交互的系統(tǒng)都得“響應(yīng)”用戶交互啊~

          但是在響應(yīng)式編程中,基于“變化傳遞”的特點(diǎn),觸發(fā)響應(yīng)的主體發(fā)生了變化。假設(shè)購(gòu)物車管理和訂單付款是兩個(gè)不同的模塊,或者至少是兩個(gè)不同的類——CartInvoice。也許我們的代碼是這樣的:

          Product.java(假設(shè)商品有兩個(gè)屬性nameprice,簡(jiǎn)單起見,price就不用BigDecimal類型了)

          public?class?Product?{
          ????private?String?name;
          ????private?double?price;
          ????//?構(gòu)造方法、getters、setters
          }

          Cart模塊中:

          import?com.example.Invoice;?//?2

          public?class?Cart?{
          ????...
          ????public?boolean?addProduct(Product?product,?int?quantity)?{
          ????????...
          ????????double?figure?=?product.getPrice()?*?quantity;
          ????????invoice.update(figure);?//?1
          ????????...
          ????}
          ????...
          }
          1. 是由Cart的對(duì)象去調(diào)用Invoice對(duì)象的更新訂單金額的方法;

          2. Cart的代碼中需要import Invoice

          b898956a5b8668bfb11d5cf75857347c.webp

          而我們?cè)儆^察這個(gè)Excel,發(fā)現(xiàn)“訂單總金額”的計(jì)算公式不僅位于自己的單元格中,而且這個(gè)公式能主動(dòng)監(jiān)聽和響應(yīng)購(gòu)物車數(shù)據(jù)的變化事件。對(duì)于購(gòu)物車來(lái)說(shuō),它沒(méi)有對(duì)訂單付款方面的任何公式引用。感覺(jué)就像這樣:

          假設(shè)數(shù)據(jù)流有操作的商品product和變化個(gè)數(shù)quantity兩個(gè)屬性:

          public?class?CartEvent?{
          ????private?Product?product;
          ????private?int?quantity;
          ????//?構(gòu)造方法、getters、setters
          }

          Invoice模塊中:

          import?com.example.Cart?//?2

          public?class?Invoice?{
          ????...
          ????public?Invoice(Cart?cart)?{
          ????????...
          ????????this.listenOn(cart);????//?1
          ????????...
          ????}
          ????//?回調(diào)方法
          ????public?void?onCartChange(CartEvent?event)?{
          ????????...
          ????}
          ????...
          }
          1. 是由Invoice的對(duì)象在初始化的時(shí)候就聲明了對(duì)Cart對(duì)象的監(jiān)聽,從而一旦Cart對(duì)象有響應(yīng)的事件(比如添加商品)發(fā)生的時(shí)候,Invoice就會(huì)響應(yīng);

          2. Invoice的代碼中import Cart

          2409e796b054a0fef4314025607f2c85.webptitle

          做過(guò)Java桌面開發(fā)的朋友可能會(huì)想到Java swing中的各種監(jiān)聽器,比如MouseListener能夠監(jiān)聽鼠標(biāo)的操作,并實(shí)時(shí)做出響應(yīng)。所以C/S的客戶端總是比B/S的Web界面更具有響應(yīng)性嘛。

          所以,這里我們說(shuō)的是一種生產(chǎn)者只負(fù)責(zé)生成并發(fā)出數(shù)據(jù)/事件,消費(fèi)者來(lái)監(jiān)聽并負(fù)責(zé)定義如何處理數(shù)據(jù)/事件的變化傳遞方式。

          那么,Cart對(duì)象如何在發(fā)生變化的時(shí)候“發(fā)出”數(shù)據(jù)或事件呢?

          1.1.2 數(shù)據(jù)流(data stream)

          這些數(shù)據(jù)/事件在響應(yīng)式編程里會(huì)以數(shù)據(jù)流的形式發(fā)出。

          我們?cè)儆^察一下購(gòu)物車,這里有若干商品,小明每次往購(gòu)物車?yán)锾砑踊蛞瞥环N商品,或調(diào)整商品的購(gòu)買數(shù)量,這種事件都會(huì)像過(guò)電一樣流過(guò)這由公式串起來(lái)的多米諾骨牌一次。這一次一次的操作事件連起來(lái)就是一串?dāng)?shù)據(jù)流(data stream),如果我們能夠及時(shí)對(duì)數(shù)據(jù)流的每一個(gè)事件做出響應(yīng),會(huì)有效提高系統(tǒng)的響應(yīng)水平。這是響應(yīng)式的另一個(gè)核心特點(diǎn):基于數(shù)據(jù)流(data stream)。

          如下圖是小明選購(gòu)商品的過(guò)程,為了既不超預(yù)算,又能省郵費(fèi),有時(shí)加有時(shí)減:

          e6864deb04545b3209b3bd57e1c85906.webp數(shù)據(jù)流

          這一次一次的操作就構(gòu)成了一串?dāng)?shù)據(jù)流。Invoice模塊中的代碼可能是這樣:

          ????public?Invoice(Cart?cart)?{
          ????????...
          ????????this.listenOn(cart.eventStream());??//?1
          ????????...
          ????}
          1. 其中,cart.eventStream()是要監(jiān)聽的購(gòu)物車的操作事件數(shù)據(jù)流,listenOn方法能夠?qū)?shù)據(jù)流中到來(lái)的元素依次進(jìn)行處理。

          1.1.3 聲明式(declarative)

          我們?cè)俚?code style="font-size:inherit;color:rgb(248,35,117);">listenOn方法去看一下:

          Invoice模塊中,上邊的一串公式被組裝成如下的偽代碼:

          ????public?void?listenOn(DataStream?cartEventStream)?{???//?1
          ????????double?sum?=?0
          ????????double?total?=?cartEventStream
          ????????????//?分別計(jì)算商品金額
          ????????????.map(cartEvent?->?cartEvent.getProduct().getPrice()?*?cartEvent.getQuantity())??//?2
          ????????????//?計(jì)算滿減后的商品金額
          ????????????.map(v?->?(v?>?199)???(v?-?40)?:?v)
          ????????????//?將金額的變化累加到sum
          ????????????.map(v?->?{sum?+=?v;?return?sum;})
          ????????????//?根據(jù)sum判斷是否免郵,得到最終總付款金額
          ????????????.map(sum?->?(sum?>?500)???sum?:?(sum?+?50));
          ????????...
          1. cartEventStream是數(shù)據(jù)流,DataStream是某種數(shù)據(jù)流類型,可以暫時(shí)想象成類似在Java 8版本增加的對(duì)數(shù)據(jù)流進(jìn)行處理的Stream API(下節(jié)會(huì)說(shuō)到為啥不用Java Stream)。

          2. map方法用于對(duì)數(shù)據(jù)流中的元素進(jìn)行映射,比如第一個(gè)將cartEvent中的商品價(jià)格和數(shù)量拿到,然后算出本次操作的金額;第二個(gè)判斷是否能享受“滿199減40”的活動(dòng)。

          這里的偽代碼用到了lambda,它非常適用于數(shù)據(jù)流的處理。沒(méi)有接觸過(guò)lambda的話沒(méi)有關(guān)系,我們后續(xù)會(huì)再聊到它。

          這是一種“聲明式(declarative)”的編程范式。通過(guò)四個(gè)串起來(lái)的map調(diào)用,我們先聲明好了對(duì)于數(shù)據(jù)流“將會(huì)”進(jìn)行什么樣的處理,當(dāng)有數(shù)據(jù)流過(guò)來(lái)時(shí),就會(huì)按照聲明好的處理流程逐個(gè)進(jìn)行處理。

          比如對(duì)于第一個(gè)map操作:

          cb94d32736d55fb34f1e03e384825e2b.webptitle

          聲明式編程范式的威力在于以不變應(yīng)萬(wàn)變。無(wú)論到來(lái)的元素是什么,計(jì)算邏輯是不變的,從而形成了一種對(duì)計(jì)算邏輯的“綁定”。

          再舉個(gè)簡(jiǎn)單的例子方便理解:

          a?=?1;
          b?=?a?+?1;
          a?=?2;

          這個(gè)時(shí)候,b是多少呢?在Java以及多數(shù)語(yǔ)言中,b的結(jié)果是2,第二次對(duì)a的賦值并不會(huì)影響b的值。

          假設(shè)Java引入了一種新的賦值方式:=,表示一種對(duì)a的綁定關(guān)系,如

          a?=?1;
          b?:=?a?+?1;
          a?=?2;

          由于b保存的不是某次計(jì)算的值,而是針對(duì)a的一種綁定關(guān)系,所以b能夠隨時(shí)根據(jù)a的值的變化而變化,這時(shí)候b==3,我們就可以說(shuō):=是一種聲明式賦值方式。而普通的=是一種命令式賦值方式。事實(shí)上,我們絕大多數(shù)的開發(fā)都是命令式的,如果需要用命令式編程表達(dá)類似上邊的這種綁定關(guān)系,在每次a發(fā)生變化并需要拿到b的時(shí)候都得執(zhí)行b = a + 1來(lái)更新b的值。

          如此想來(lái),“綁定美元政策”不也是一種聲明式的范式嗎~

          總結(jié)來(lái)說(shuō),命令式是面向過(guò)程的,聲明式是面向結(jié)構(gòu)的。

          不過(guò)命令式和聲明式本身并無(wú)高低之分,只是聲明式比較適合基于流的處理方式。這是響應(yīng)式的第三個(gè)核心特點(diǎn):聲明式(declarative)。結(jié)合“變化傳遞”的特點(diǎn),聲明式能夠讓基于數(shù)據(jù)流的開發(fā)更加友好。

          1.1.4 總結(jié)

          總結(jié)起來(lái),響應(yīng)式編程(reactive programming)是一種基于數(shù)據(jù)流(data stream)和變化傳遞(propagation of change)的聲明式(declarative)的編程范式。

          響應(yīng)式編程的“變化傳遞”就相當(dāng)于果汁流水線的管道;在入口放進(jìn)橙子,出來(lái)的就是橙汁;放西瓜,出來(lái)的就是西瓜汁,橙子和西瓜、以及機(jī)器中的果肉果汁以及殘?jiān)龋际橇鲃?dòng)的“數(shù)據(jù)流”;管道的圖紙是用“聲明式”的語(yǔ)言表示的。

          這種編程范式如何讓W(xué)eb應(yīng)用更加“reactive”呢?

          我們?cè)O(shè)想這樣一種場(chǎng)景,我們從底層數(shù)據(jù)庫(kù)驅(qū)動(dòng),經(jīng)過(guò)持久層、服務(wù)層、MVC層中的model,到用戶的前端界面的元素,全部都采用聲明式的編程范式,從而搭建一條能夠傳遞變化的管道,這樣我們只要更新一下數(shù)據(jù)庫(kù)中的數(shù)據(jù),用戶的界面上就相應(yīng)的發(fā)生變化,豈不美哉?尤其重要的是,一處發(fā)生變化,我們不需要各種命令式的調(diào)用來(lái)傳遞這種變化,而是由搭建好的“流水線”自動(dòng)傳遞。

          這種場(chǎng)景用在哪呢?比如一個(gè)日志監(jiān)控系統(tǒng),我們的前端頁(yè)面將不再需要通過(guò)“命令式”的輪詢的方式不斷向服務(wù)器請(qǐng)求數(shù)據(jù)然后進(jìn)行更新,而是在建立好通道之后,數(shù)據(jù)流從系統(tǒng)源源不斷流向頁(yè)面,從而展現(xiàn)實(shí)時(shí)的指標(biāo)變化曲線;再比如一個(gè)社交平臺(tái),朋友的動(dòng)態(tài)、點(diǎn)贊和留言不是手動(dòng)刷出來(lái)的,而是當(dāng)后臺(tái)數(shù)據(jù)變化的時(shí)候自動(dòng)體現(xiàn)到界面上的。

          兩年嘔心瀝血的文章「面試題」「基礎(chǔ)」「進(jìn)階」這里全都有!


          200多篇原創(chuàng)技術(shù)文章海量視頻資源精美腦圖面試題

          長(zhǎng)按掃碼可關(guān)注獲取?

          在看和分享對(duì)我非常重要!5c49e48af8032c08e34c0b9ed8b13858.webp

          瀏覽 67
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  亚洲淫秽视频在线 | 亚洲AV最新 | 亚洲AV在线免费观看 | 无码在线观看播放 | 五月丁香婷婷六月 |