subtitle: Ken 與 FP 的旅程 ~ 2021-03-31
前言
一直以來都是對 procedure programming 和 OOP 比較熟悉,粗略地知道 functional programming 的概念,也有使用這樣的程式設計法,但沒有再進一步了解,機緣下聽到了 Ken 的這場分享,以下是我聽完分享後的一些思考。
先來一段情境
(pipe)
賣場上有三種折價券:98 折、95 折、92 折,沒有限制,可以同時使用。
如果用程式表達這三種折價券可以用三個函式:
const useCoupon98 = x => x*0.98;
const useCoupon95 = x => x*0.95;
const useCoupon92 = x => x*0.92;
如果用 procedure programming 描述我用三張折價券買了一本書:
let book = 400;
let result = useCoupon98(book)
result = useCoupon95(book)
result = useCoupon92(book)
好像也沒有不對,但在這個情境裡,也許用 functional 的方法思考會更符合情境。因為折扣卷並沒有一定要先使用哪一張,但這樣的寫法默默地寫下了執行順序。
如果使用 functional prgramming,似乎會好一點:
let book = 400;
let useCoupons = (a,b,c) => x => a(b(c(x))); //為方便使用固定三個參數,可以改成不定參數
let useCoupons(useCoupon98,useCoupon95,useCoupon92)(book)
雖然這個 useCoupons 並不完美,它裡面的實作依舊是按照順序呼叫,但這個函式已經成功將使用折價券的過程封裝起來,對使用者來說只知道它可以接收折價券當參數。
所以第一種寫法不好嗎?
還記得剛開始學習函式時,學到使用函式可以減少重複的程式碼,讓程式碼更好維護。
另外,也能透過函式命名讓程式更好懂(封裝流程?)。
再來換個情境看看,現在要做一顆切換顏色的按鈕。
範例示範用原生 js 與一步一步將一顆按鈕掛進 container,與用 React hook 方法的差別
const container = document.getElementById('container')
const btn = document.createElement('button')
btn.className ="gray"
btn.onClick = function(e){
if(this.classList.conatins('gray')){
this.classList.remove('gray')
this.classList.add('red')
}else{
this.classList.remove('red')
this.classList.add('gray')
}
}
container.appendChild(btn)
如果用流程的方式思考:
- 我們必須先創造出一顆按鈕,才能加上 class。
- 判斷狀態時必須先判斷是灰色才能改成紅色
如果有很多顆按鈕就不好維護這段判斷顏色的程式了,於是想把這段程式抽給函式也很合理:
const changeColor = (prevColor, activeColor) => {
if(this.classList.conatins(prevColor)){
this.classList.remove(prevColor)
this.classList.add(activeColor)
}else{
this.classList.remove(prevColor)
this.classList.add(activeColor)
}
}
順便再加個變數。
很基本,所有學到函式與變數的人都能輕鬆做到。
const container = document.getElementById('container')
const btn = document.createElement('button')
btn.className ="gray"
btn.onClick = function(e){
changeColor('gray','red')
}
container.appendChild(btn)
現在,可以來看看 React 在做的事,其實也就是把整個過程要用到的命令式寫法,都抽成函式。
const Container = (props) => {
const [color, setColor] = useState('gray')
const changeColor = () => {
setColor( prevColor => prevColor === 'gray' ? 'red' : 'gray')
}
return (
<button className="${color}" onClick={changeColor} >click</button>
)
}
抽出函式這件事從很久以前就學過了,但以往是有大量重複的地方或流程很長才會想到用函式。
但 functional programming 的思考,似乎是沒有必要的命令(沒有順序分枝?)都應該抽成函式。
procedure 的方式,在控制流程分枝時相當好用。
但 if ... else ... 遇到狀態時,就相當棘手了。
在不需要流程控制時,應該以 functional programming 思考。functional progamming 會在前端流行起來,也許是做前端需要大量維護狀態。
大概吧。
有空再研究ㄌ