If you can’t describe what you are doing as a process, you don’t know what you’re doing.
— W. Edwards Deming (attr)
核心概念#
所有程式都在轉換資料——將輸入轉換為輸出。然而當我們思考設計時,我們很少想到創建轉換。我們擔心的是類別和模組、資料結構和演算法、語言和框架。
作者認為這種對程式碼的聚焦經常偏離了重點:我們需要回到將程式視為將輸入轉換為輸出的思維。當我們這樣做時,許多之前擔心的細節都會消失。結構變得更清晰、錯誤處理更一致、耦合大幅降低。
Tip 49 - Programming Is About Code, But Programs Are About Data(程式設計關乎程式碼,但程式關乎資料)
Unix 管線:最早的轉換#
1970 年代的 Unix 程式設計師會這樣思考:列出目錄樹中最長的五個檔案。
$ find . -type f | xargs wc -l | sort -n | tail -6 | head -5這是一系列的轉換:
- 目錄名稱 -> 檔案列表 -> 帶行數的列表 -> 排序後的列表 -> 最高五個 + 總計 -> 最高五個
就像工業生產線:一端送入原始資料,另一端出來成品(資訊)。
尋找轉換#
有時找到轉換最簡單的方式是從需求開始,確定其輸入和輸出。現在你已經定義了代表整個程式的函式。然後你可以找出從輸入到輸出的步驟。這是一種由上而下(top-down) 的方法。
範例:字謎搜尋器#
輸入是一組字母,輸出是按長度分組的單字列表。整個程式分解為四個獨立的轉換:
- 所有三個字母以上的組合
- 組合的簽章(sorted letters)
- 所有匹配簽章的字典單字
- 按長度分組
每個轉換可以再分解為更小的轉換(Transformations All the Way Down)。
管線運算子 |>#
Elixir 等函數式語言有 pipeline operator(管線運算子)。它只是把左邊的值插入作為右邊函式的第一個參數:
"vinyl" |> String.codepoints |> Comb.subsets()等同於:
Comb.subsets(String.codepoints("vinyl"))使用管線意味著你自動以轉換資料的方式思考——每次看到 |>,你實際上看到的是資料從一個轉換流向下一個的地方。
許多語言有類似的功能:Elm、F#、Swift 有
|>,Clojure 有->和->>,R 有%>%,Haskell 有管線運算子。即使你的語言沒有管線,你仍然可以用一系列賦值語句來表達轉換思維。
為什麼這很棒#
將主函式串在一起:
word
|> all_subsets_longer_than_three_characters()
|> as_unique_signatures()
|> find_in_dictionary()
|> group_by_length()這就是滿足需求所需的轉換鏈,每個從前一個轉換接收輸入並傳遞輸出到下一個。這幾乎是你能得到的最接近文學式程式碼(literate code)的東西。
Tip 50 - Don’t Hoard State; Pass It Around(不要囤積狀態;傳遞它)
在轉換模型中,不是把一小堆一小堆的資料散佈在整個系統中,而是把資料想像成一條大河、一個流(flow)。資料成為功能的夥伴:管線是一系列 code -> data -> code -> data...。資料不再綁定於特定的函式群組(如類別定義中那樣),而是自由地代表應用程式將輸入轉換為輸出的展開過程。
這意味著我們可以大幅降低耦合:一個函式可以在任何其參數匹配另一個函式輸出的地方使用(和重用)。
錯誤處理#
如果我們只能建構線性管線,要怎麼加入錯誤處理的條件邏輯呢?
基本慣例是:我們永遠不在轉換之間傳遞原始值。而是將它們包裝在一個資料結構(或類型)中,該結構也告訴我們包含的值是否有效。在 Haskell 中這叫 Maybe,在 F# 和 Scala 中叫 Option。
兩種處理方式#
- 在每個轉換內部處理:每個函式接受
{:ok, value}或{:error, reason}tuple,如果收到 error 就直接傳遞 - 在管線中處理:使用
and_then這樣的 bind 函式——它接受包裝的值,如果是:ok就解開並套用函式,如果是:error就直接回傳
def and_then({:ok, value}, func), do: func.(value)
def and_then(anything_else, _func), do: anything_else轉換改變程式設計#
以一系列(巢狀的)轉換來思考程式碼,是一種解放式的程式設計方法。習慣需要一段時間,但一旦養成這個習慣,你會發現程式碼變得更乾淨、函式更短、設計更扁平。
相關章節#
- Topic 8,好設計的本質
- Topic 17,Shell 遊戲
- Topic 26,如何平衡資源
- Topic 28,去耦合
- Topic 35,Actor 與 Process