一汽纠、核心思想
函數(shù)式編程語言,讓我們用高階抽象取代基本的控制結(jié)構(gòu)丧叽。
面向?qū)ο缶幊掏ㄟ^封裝不確定因素使代碼被人理解;函數(shù)式編程通過盡量減少不確定因素使代碼被人理解骚灸。 ---- Micheal Feathers
面向?qū)ο缶幊蹋褐赜玫膯卧穷惡皖愔g的溝通消息(方法)。OOP提倡開發(fā)者針對具體問題建立專門的數(shù)據(jù)結(jié)構(gòu)驴一,以專門的操作(方法)附在數(shù)據(jù)結(jié)構(gòu)上休雌。
函數(shù)式編程:提倡在有限幾種關(guān)鍵數(shù)據(jù)結(jié)構(gòu)(List,Map肝断,set)上針對這些數(shù)據(jù)結(jié)構(gòu)進(jìn)行高度優(yōu)化的操作(方法)杈曲,以此構(gòu)成基本運(yùn)轉(zhuǎn)機(jī)制。開發(fā)者根據(jù)具體的用途插入自己的數(shù)據(jù)結(jié)構(gòu)和高階函數(shù)去調(diào)整整體機(jī)制的運(yùn)轉(zhuǎn)方式胸懈。
二担扑、思維轉(zhuǎn)變
學(xué)習(xí)新的語言,把熟悉的概念用新的語法表達(dá)出來趣钱。
學(xué)習(xí)新的范式涌献,為熟悉的問題找到新的解答方法。
函數(shù)式編程首有,需要站在更高的抽象層次上工作洁奈。
命令式編程:按照程序是一系列狀態(tài)改變的命令
來建模的一種編程風(fēng)格。例如 for 循環(huán) 绞灼,先確認(rèn)初始狀態(tài)利术,每次都執(zhí)行每次迭代循環(huán)體中的一系列命令。命令式編程鼓勵將操作安排在循環(huán)內(nèi)部進(jìn)行低矮。
函數(shù)式編程:將程序描述為表達(dá)式和變換印叁,以數(shù)學(xué)的方式建立模型,盡量避免可變的狀態(tài)军掂。
高階函數(shù)消除了摩擦
函數(shù)式的基本構(gòu)造單元
- 篩選 (filter)
需要根據(jù)篩選條件產(chǎn)生一個子集合 使用filter - 映射(map)
對原集合中的每一個元素執(zhí)行給定的函數(shù),從而變成一個新的集合. - 折疊和化約(fold,reduce)
用一個累積量來"收集"集合元素.比如fold的含義為用一個二元函數(shù)或者云算符結(jié)合列表的首元素和累積量的初始值,重復(fù)上一步直到列表耗盡,此時累積量的取值為折疊運(yùn)算的結(jié)果.
需要把一個集合分成一小塊一小塊的來處理,使用fold或reduce
三轮蜕、權(quán)責(zé)讓渡
抽象的目的總是一樣的:讓開發(fā)者從繁瑣的細(xì)節(jié)中解脫出來,去解答問題中非重復(fù)性的那些部分蝗锥。
1.迭代讓位高階函數(shù)
2.閉包
閉包:是一種奇特的函數(shù)跃洛,他把需要引用的內(nèi)部變量都放在上下文里“包”起來了。換句話說终议,對于所需的內(nèi)部變量可以讓調(diào)用者靈活創(chuàng)建汇竭。一般閉包包括兩個步驟,創(chuàng)建綁定和執(zhí)行穴张。
Groovy中閉包示例:
class Employee {
def name, salary
}
//創(chuàng)建閉包
def paidMore(amount) {
return { Employee e -> e.salary > amount }
}
//綁定閉包
isPaidMore=paidMore(10000)
//執(zhí)行閉包
def lily = new Employee("Lily", 15000)
println(isPaidMore(lily))
//綁定閉包
isPaidMore2=paidMore(20000)
//執(zhí)行閉包
def lucy = new Employee("Lucy", 20000)
println(isPaidMore2(lucy))
這里面 paidMore就是一個閉包细燎,isPaidMore,isPaidMore2是閉包的實(shí)例皂甘。
每個閉包實(shí)例中保有自己的一份變量取值玻驻,包括私有變量也是如此。
閉包經(jīng)常被函數(shù)式語言和框架當(dāng)做一種異地執(zhí)行的機(jī)制,用來傳遞待執(zhí)行的變換代碼偿枕,例如map之類的高階函數(shù)
-
科里化和函數(shù)的部分施用
科里化:如果你固定某些參數(shù)璧瞬,你將得到接受余下參數(shù)的一個函數(shù)户辫。對于一個兩個可變量的函數(shù)xy,如果固定y=2嗤锉,將得到有一個可變量的函數(shù)2x渔欢。一般情況f(x,y,z)=>f(x)(y)(z) 把函數(shù)多個參數(shù)變成一連串單個參數(shù)的變換。
部分施用:提前帶入一部分參數(shù)值档冬,使得多個參數(shù)的函數(shù)得以省略部分參數(shù)膘茎,從而得到單個參數(shù)的函數(shù)桃纯。先求解一部分參數(shù)酷誓,返回由剩下參數(shù)構(gòu)成簽名的函數(shù)。一般情況态坦,如果在f(x,y,z)上部分施用一個參數(shù)盐数,將得剩余兩個參數(shù)的函數(shù)f(y,z)。
3.科里化和部分施用一般用途
1.函數(shù)工廠
在傳統(tǒng)面向?qū)ο?/p>
2.模板方法模式
GoF的模板方法(Template Method)的用意是在固定的算法框架內(nèi)部安排一些抽象方法伞梯,為后續(xù)具體實(shí)現(xiàn)保留一部分靈活行玫氢。部分施用先固定一部分參數(shù)(注入當(dāng)前已確定的行為),留下未確認(rèn)的參數(shù)給具體實(shí)現(xiàn)去發(fā)揮谜诫,思路和模板方法如出一轍漾峡。
3.隱含參數(shù)
當(dāng)我們需要頻繁調(diào)用一個函數(shù),而每次參數(shù)值都差不多時喻旷,使用科里化來設(shè)置隱含參數(shù)生逸。【把參數(shù)值固定】
4.遞歸
遞歸:“以一種自相似的方式來重復(fù)事物的過程”且预。
是運(yùn)行時托付細(xì)節(jié)的一個例子槽袄,而且和函數(shù)式編程有著緊密聯(lián)系。
四. 函數(shù)的兩種常見特性
函數(shù)式編程的構(gòu)造目的:從頻繁出現(xiàn)的場景中 消滅掉煩人的細(xì)節(jié)
1.記憶
指的是在函數(shù)級別上對多次需要使用的值進(jìn)行緩存的策略.
緩存兩種用法:
- 內(nèi)部緩存
- 外部緩存
緩存的兩種實(shí)現(xiàn)方式
- 手工實(shí)現(xiàn)狀態(tài)管理
- 自動記憶機(jī)制
2.引入記憶
記憶函數(shù),使用了所謂"元函數(shù)"技法(操作的對象是函數(shù)本身,而非函數(shù)結(jié)果)
3.緩求值
緩求值的集合不會預(yù)先算好所有的元素,而是在需要用到的時候才落實(shí)下來.
有3大好處:
- 昂貴的運(yùn)算只有到了絕對必要時候才執(zhí)行
- 可以建立無限大集合,只要一有請求,就一直送出元素
- 按緩求值的方式來使用映射,過濾等函數(shù)可以產(chǎn)生高效代碼
4.構(gòu)造緩求值列表
對于嚴(yán)格求值的語言,如果使用閉包遞歸的將一個嚴(yán)格求值列表層層包裹起來,就可以將之變成緩求值列表.現(xiàn)實(shí)中會避免施用遞歸.
五.模式與重用
大多數(shù)現(xiàn)代語言是多范式,支持對象,函數(shù)式,元對象,以及其他多樣化范式.
在不同的范式中表現(xiàn)出來的設(shè)計(jì)模式,可能呈現(xiàn)截然不同的外在形象.這是因?yàn)楹瘮?shù)式世界用來搭建程序的材料不一樣,所以解決問題的手段也不同.GoF的模式有一部分傳統(tǒng)模式失去了存在的意義,還有一部分,他們要解決的問題還存在,只是解決手段不一樣.
傳統(tǒng)的設(shè)計(jì)模式在函數(shù)式編程世界中大致有3中歸宿:
- 模式已經(jīng)被吸收成語言的一部分
- 模式中描述解決的辦法依然還成立,具體的實(shí)現(xiàn)細(xì)節(jié)有變化
- 由于新語言或者范式獲得了原本沒有的能力,有了新的解決方案.(例如元編程)
1.函數(shù)級別的重用
復(fù)用(composition),作為一種重用機(jī)制,在函數(shù)式語言中主要表現(xiàn)為:通過參數(shù)傳遞函數(shù).與面向?qū)ο笳Z言相比,函數(shù)式語言重用發(fā)生在粗粒度的級別上,著眼提取共通的運(yùn)行機(jī)制,并參數(shù)化調(diào)整其行為.
函數(shù)式的重用機(jī)制建立在列表的概念,以及可以連同上下文一期傳遞的代碼塊的概念之上.
函數(shù)式語言作為第一等成分的函數(shù),可以充當(dāng)參數(shù)和返回值.
- 函數(shù)式模板模式(Template Method)
- Strategy模式
- Flyweight 模式和記憶
- 單例模式
- Factory模式和科里化
2.結(jié)構(gòu)化重用和函數(shù)式重用的對比
1.以結(jié)構(gòu)為載體的代碼重用
如果面向?qū)ο蟮拇a值得重用,我們一般會把他提取到另一個類中,然后通過繼承來訪問它.
六.現(xiàn)實(shí)應(yīng)用
- 函數(shù)式接口
含有單一方法的接口,稱為函數(shù)式接口SAM(single Abstract Method),runnable 和callable接口就是有代表性的例子.在java中允許通過lambda 和SAM攜手. - Optional類型
Optional 是一種結(jié)構(gòu),防止返回結(jié)果出現(xiàn)null - Stream
函數(shù)式的基礎(chǔ)設(shè)施
在數(shù)據(jù)庫,軟件架構(gòu)上函數(shù)是思維的作用?
1.架構(gòu)
函數(shù)式的架構(gòu)從根本上貫徹"值不可變"的思路,最大的發(fā)揮其優(yōu)點(diǎn).學(xué)會從值不變的角度去思考,是我們掌握函數(shù)式思維的一條重要門徑.
- CQRS
傳統(tǒng)應(yīng)用 程序架構(gòu)把讀寫數(shù)據(jù)交織一起.例如
壞處:模型需要負(fù)責(zé)處理業(yè)務(wù)規(guī)則和驗(yàn)證的工作,一般模型對象還要在內(nèi)部或通過另外的邏輯層來協(xié)調(diào)持久化事項(xiàng).開發(fā)者需要兼顧讀寫兩方面潛在的影響,增加復(fù)雜性.
CQRS 通過分離架構(gòu)中負(fù)責(zé)讀取的部分和負(fù)責(zé)命令的部分,部分的簡化了程序的架構(gòu).
架構(gòu)永遠(yuǎn)是取舍的結(jié)果,CQRS簡化了一些方面,同時又復(fù)雜話了另一些方面.例如對于集中式數(shù)據(jù)庫來說,事務(wù)處理并不難.可是在CQRS架構(gòu)下,我們可能需要用最終一致性模型來取代事務(wù)性的模型.
最終一致性:是分布式中的一種模型,不對模型的變更操作施加硬性的時間限制,而只保證,當(dāng)更新發(fā)生后,模型最終回復(fù)到一致的狀態(tài).
2.Web框架
web領(lǐng)域與函數(shù)式編程簡直是天作之合.這些web框架大多具備以下共同特性:
- 路由框架
從應(yīng)用主體中剝離路由的相關(guān)細(xì)節(jié),將之交托專門的路由功能庫. - 以函數(shù)作為路由的目標(biāo)
把路由理解成一個接收request,返回response的函數(shù). - 領(lǐng)域?qū)S妙A(yù)言(DSL)
MartinFowler 將DSL定義為表達(dá)能力有限,專門針對一個狹窄問題領(lǐng)域的計(jì)算機(jī)編程語言.函數(shù)式預(yù)言偏好描述性的代碼風(fēng)格這一點(diǎn)恰好也常常是DSL的目的. - 與構(gòu)建工具緊密集成
總體來說,不可變的值編程,會降低測試的負(fù)擔(dān),因?yàn)樾枰ㄟ^測試來驗(yàn)證的狀態(tài)變化少了.
七.多語言與多范式
現(xiàn)代編程語言常常是多范式的,支持多種多樣的編程范式,如面向?qū)ο?元編程,函數(shù)式,過程式等等.
正交 在數(shù)學(xué)里吧兩個相互垂直的向量稱作正交,也就是說這兩個向量不相關(guān)(不會相交).在計(jì)算機(jī)科學(xué)里,把兩個組件如果沒有相互沒有任何影響(或副作用),就可以稱作是正交的.例如在groove,kotlin 中使用元編程并不妨礙我們使用函數(shù)式編程的構(gòu)造,反之亦然.
2.多范式預(yù)言的后顧之憂
由于預(yù)言支持各式各樣的抽象和概念,由不同的開發(fā)群體制作出來的庫也會呈現(xiàn)明顯的差異.不同范式的思路是不一樣的.可以運(yùn)用"消費(fèi)者驅(qū)動的契約",(以測試的形式)在不同團(tuán)隊(duì)之間簡歷可執(zhí)行的契約.
消費(fèi)者驅(qū)動的契約:
一項(xiàng)集成工作的實(shí)施方與各組件供應(yīng)方共同商定的一組測試.集成方"同一",如果一方需要打破前設(shè)置條件,必須著急所受歐影響的當(dāng)事方,議定一組新的測試.
3.上下文型抽象與復(fù)合型抽象對比
復(fù)合是函數(shù)是編程領(lǐng)域奉為圭臬的設(shè)計(jì)原則.
上下文型是 基于插件的架構(gòu)使用的設(shè)計(jì)原則.
4.函數(shù)是金字塔
進(jìn)行語言的選型
不應(yīng)該從靜態(tài)類型中求索抵御錯誤的能力,從根本上擁抱函數(shù)式的概念才是正確方向.假如包括數(shù)據(jù)訪問,集成等重要職責(zé)在內(nèi)的核心api,都能以值不變性為前提來設(shè)計(jì)的話,那么所有的代碼都對大幅度地簡化.當(dāng)然在這思路下,數(shù)據(jù)庫和其他基礎(chǔ)設(shè)施的構(gòu)建方式也需要隨之放生變化.
在函數(shù)式內(nèi)核之上,用命令式語言編寫系統(tǒng)中對開發(fā)效率要求較高的部分,例如工作流,業(yè)務(wù)規(guī)則,用戶界面.最上層和原來模型一樣是 DSL層,DSL會貫穿系統(tǒng)的所有層次,一致深入到最底層.