1 of 29

簡約的軟體開發思維 4~5

前端讀書會/上德/2025-10-3

B L U E P L A N E T

2 of 29

章節

4. 擷取 Actions 函式中的 Calculations

5. 改良 Actions 的設計

​

2

B L U E P L A N E T

3 of 29

復習 1

  • Actions:任何會受執行時間(順序(ordering))、或執行次數(重複(repetition))影響的程式碼 一般稱為非純函數 (impure function)
  • Calculations:程式碼利用輸入推導出輸出,如果輸入相同、輸出也必定相同,無論何時何地進行呼叫都不受影響。也稱為純函數 (pure function)、數學函數
  • Data:是與各事件有關的事實紀錄

3

B L U E P L A N E T

4 of 29

復習 2

  • 今日大多數的軟體開發皆採分散式系統,掌握軟體如何隨時間變化是困難且重要的當務之急
  • FP程式設計師的偏好:Data > Calculations > Actions
    • Data 和 Calculations 程式碼不會受到執行時機或存取次數影響
    • Actions 需要考慮其 side-effects
    • Actions 其時間依賴性會在程式中擴散

4

B L U E P L A N E T

5 of 29

Chapter 4. 擷取 Actions 函式中的 Calculations

5

B L U E P L A N E T

6 of 29

範例 – 網路商城

  1. 將商品加入購物車
  2. 計算購物車總金額→修改總金額 DOM
  3. 逐一走訪按鈕→判斷是否免運→顯示免運標籤 DOM
  4. 計算稅金→更新 DOM

​

​

6

B L U E P L A N E T

7 of 29

範例程式碼

每一個函式分別是 Actions, Calculations, Data?

7

?

?

?

?

?

?

B L U E P L A N E T

8 of 29

範例程式碼

每一個函式分別是 Actions, Calculations, Data?

→ 每一個都是 A

8

B L U E P L A N E T

9 of 29

可測試性

  • 可測試性的問題:
    • 每次程式碼發生變化,測試員都得重複所有的操作流程
    • 各別 function 測試之前,必須先設定全域變數
  • 建議:
    • 將關鍵算式與 DOM 更新分開
    • 把全域變數去掉

9

B L U E P L A N E T

10 of 29

可重覆性

  • 公司的會計與運輸部門想使用我們的程式,卻遇到以下阻礙:
    • 程式是從全域變數取得購物車總額,但會計與運輸部門須根據資料庫來處理金額
    • 本程式直接把稅金更新到 DOM,但上述兩部門需將稅金印在收據與貨物標籤上
  • 建議:
    • 消除程式對全域變數的依賴
    • 不應該假設只有 DOM 需要程式輸出
    • 將函式的結果傳回

10

B L U E P L A N E T

11 of 29

函式的輸入、輸出

  • 顯性輸入 (explicit inputs):引數 (arguments)
  • 隱性輸入:除引數以外的輸入
  • 顯性輸出:傳回值
  • 隱性輸出:除傳回值以外的輸出

* 在 FP 的語言裡,隱性輸入與輸出即是 side effects,他們有別於函式的主要作用 (effects) - 計算傳回值

11

B L U E P L A N E T

12 of 29

移除隱性輸入和輸出

  • 上述可測試性及可重覆性的建議都與「移除隱性輸入和輸出」有關:
    • 建議1:將關鍵算式與 DOM 更新分開 → 將關鍵算式與隱性輸出(更新 DOM)分開
    • 建議2:把全域變數去掉 → 移除隱性輸入與輸出
    • 建議3:消除程式對全域變數的依賴 → 去除與全域變數有關的隱性輸入和輸出
    • 建議4:不應該假設只有 DOM 需要程式輸出 → 以傳回值取代隱性輸出
    • 建議5:將函式的結果傳回 → 用顯性輸出取代隱性輸出

12

B L U E P L A N E T

13 of 29

從 Action 中擷取出 Calculation

  • 擷取子程序 (extract subroutine) - 將運算的程式獨立出來
  • 去除隱性輸入 - 所有輸入換成引數
  • 去除隱性輸出 - 輸出換成傳回值

13

B L U E P L A N E T

14 of 29

擷取子程序

將運算的程式獨立出來

14

B L U E P L A N E T

15 of 29

去除隱性輸出

輸出換成傳回值

15

B L U E P L A N E T

16 of 29

去除隱性輸入

所有輸入換成引數

16

B L U E P L A N E T

17 of 29

copy-on-write

  • 傳入引數與傳回值必須具備不變性
  • 先將可變資料拷貝一份再做修改是實作不變性(immutability)的方法之一

17

B L U E P L A N E T

18 of 29

Chapter 5. 改良 Actions 的設計

18

B L U E P L A N E T

19 of 29

設計原則1:最小化隱性輸入與輸出

  • Actions中的隱性輸入/輸出不可能完全去除,但能消除得越多結果越好
  • 隱性輸入/輸出的影響:
    • 隱性輸入會限制呼叫函式的時機
    • 隱性輸出也會對函式呼叫產生同樣的限制
  • 由於上述限制,擁有隱性輸入與輸出的函式:
    • 不具備模組化 (modular) 特性,該函式不能用在其他地方
    • 較難測試

19

B L U E P L A N E T

20 of 29

減少隱性輸入/輸出

20

B L U E P L A N E T

21 of 29

根據用途選擇合適的抽象化層級

  • 我們想知道的是「某一筆訂單」是否享有免運優惠,但該函式考量的卻是「購物車總額加上某商品價格」

​

​

​

​

​

* 更改函式簽章(引數)並非重構,因為函式行為會改變

21

B L U E P L A N E T

22 of 29

設計原則2:「拆解」是設計的本質

拆解成小函式的好處:

  • 可重複使用性提升
  • 維護更方便
  • 測試更簡單

22

B L U E P L A N E T

23 of 29

替 Calculations 分類

  • 範例 - 購物車:
    • C:購物車陣列操作
    • I:單項商品操作
    • B:業務規則
  • 最終會擴展為程式的意義分層 (layers of meaning)(後續章節)

23

B L U E P L A N E T

24 of 29

依分類拆解

依「購物車陣列」和「商品物件」拆解:

​

(名稱普適化)

​

24

B L U E P L A N E T

25 of 29

程式重構後

25

B L U E P L A N E T

26 of 29

小結

26

B L U E P L A N E T

27 of 29

Recap

  • 進行重構將 Calculations 從 Actions 中擷取出來,增加程式的可測試性與可重複使用性
  • 重構步驟:
    1. 將運算的程式獨立出來(擷取子程序 )
    2. 去除隱性輸入/輸出(所有輸入換成引數/輸出換成傳回值)
  • Actions中的隱性輸入/輸出不可能完全去除,但能消除得越多結果越好
  • 「拆解」是設計的本質:未經設計的程式→拆解→再組合

27

B L U E P L A N E T

28 of 29

我的心得

  • 本書的思維架構很有趣,他用了自己提出的名詞來取代一些 FP 理論中會使用到的術語(書裡只用註解的方式提到),比如:
    • Actions → Impure function
    • Calculations → Pure function
    • 顯性輸入/輸出 (explicit inputs/outputs) → Side Effects
  • 這兩章雖然不難,但他要說明的東西都是由範例程式帶出來的,程式碼不要跳過一起讀完還是會比較有 fu,會建議有時間的話可以完整閱讀(而且第4章開始的範例似乎會一直延續到後續的章節)

28

B L U E P L A N E T

29 of 29

Thanks

29

B L U E P L A N E T