Skip to content

Chapter 1 ~ 5

Source

書籍:簡約的軟體開發思維:用 Functional Programming 重構程式 - 以 JavaScript 為例

FP 基本的重要概念

  • 區分 Actions、Calculations、Data 是基本的重要技能。

Actions(動作、操作或行為)

  • 了解如何將 Actions 拆解成 Calculations、Data 與其他小 Actions。(拆解需要適可而止)。

Calculations(計算、運算)

Caculations 就是能將輸入轉換為輸出的運算。只要輸入相同,就一定會給出一樣的答案,無論什麼時候或呼叫幾次。

  • Calculations 可能由其他小的 Calculations 與 Data 組成(這部分我想到 Vue 中 store state 跟 getter 的關係)。
  • Calculations 容易被忽略,可以換個角度想,決策與計劃所在的地方就可能有 Calculations。

Data(資料、數據)

函式的輸入與

  • 隱性輸入:所有非引數的輸入
  • 隱性輸出:所有非回傳值的輸出
  • Calculations 不能包含隱性輸入/輸出。
  • 『盡量避免隱性輸入/輸出』的原則也適用於 Actions。

以上可參考 page 5-5

練習 4-2

把 update_tax_dom() 中的 Calculation 截取出來。

截取前:

javascript
function update_tax_dom() {
  set_tax_dom(shopping_cart_total * 0.10)
}

截取後:

javascript
function update_tax_dom() {
  const totalWithTax = calcTax(shopping_cart_total)
  set_tax_dom(totalWithTax)
}

function calc_tax(price_without_tax) {
  return priceWithoutTax * 0.10
}

截取後,計算 tax 的關鍵算式可以跟 DOM 更新脫鉤,關鍵算式 calcTax 會是一個 pure function。

練習 4-4

把 update_shipping_icons() 中的 Calculation 截取出來。

截取前:

javascript
function update_shipping_icons() {
  const buy_buttons = get_buy_buttons_dom()
  for (let i = 0; i < buy_buttons.length; i++) {
    const button = buy_buttons[i]
    const item = button.item
    if (item.price + shopping_cart_total >= 20)
      button.show_free_shipping_icon()
    else
      button.hide_free_shipping_icon()
  }
}

截取後:

javascript
function update_shipping_icons() {
  const buy_buttons = get_buy_buttons_dom()
  for (let i = 0; i < buy_buttons.length; i++) {
    const button = buy_buttons[i]
    const item = button.item
    if (get_free_shipping(item.price, shopping_cart_total))
      button.show_free_shipping_icon()
    else
      button.hide_free_shipping_icon()
  }
}

function get_free_shipping(item_price, total) {
  return item_price + total >= 20
}

範例 5.9 擷取『寫入時複製』模式

修改前:

javascript
function add_item(cart, item) {
  const new_cart = cart.slice()
  new_cart.push(item)
  return new_cart
}

名稱普適化後:

javascript
function add_element_last(array, elem) {
  const new_array = array.slice()
  new_array.push(elem)
  return new_array
}

function add_item(cart, item) {
  return add_element_last(cart, item)
}

add_element_last 就變成一個可以處理任何陣列與元素的通用 function。

練習 5-2

將以下 function 拆解成多個 function,使得每個 function 只做一種操作

javascript
function update_shipping_icons(cart) {
  const buy_buttons = get_buy_buttons_dom()
  for (let i = 0; i < buy_buttons.length; i++) {
    const button = buy_buttons[i]
    const item = button.item
    const new_cart = add_item(cart, item)
    if (gets_free_shipping(new_cart))
      button.show_free_shipping_icon()
    else
      button.hide_free_shipping_icon()
  }
}

拆解後,把『按鈕操作』與『購物車/商品操作』分開。:

javascript
function gets_free_shipping_with_item(cart, item) {
  const new_cart = add_item(cart, item)
  return gets_free_shipping(new_cart)
}

function set_free_shipping_icon(button, isShown) {
  if (isShown)
    button.show_free_shipping_icon()
  else
    button.hide_free_shipping_icon()
}

function update_shipping_icons(cart) {
  const buy_buttons = get_buy_buttons_dom()
  for (let i = 0; i < buy_buttons.length; i++) {
    const button = buy_buttons[i]
    const item = button.item
    const hasFreeShipping = gets_free_shipping_with_item(cart, item)

    set_free_shipping_icon(hasFreeShipping)
  }
}