簡約的軟體開發思維:用 Functional Programming 重構程式 - 以 Javascript 為例 (Grokking Simplicity: Taming Complex Software with Functional Thinking)

Eric Normand 著 黃駿 譯

  • 簡約的軟體開發思維:用 Functional Programming 重構程式 - 以 Javascript 為例 (Grokking Simplicity: Taming Complex Software with Functional Thinking)-preview-1
  • 簡約的軟體開發思維:用 Functional Programming 重構程式 - 以 Javascript 為例 (Grokking Simplicity: Taming Complex Software with Functional Thinking)-preview-2
  • 簡約的軟體開發思維:用 Functional Programming 重構程式 - 以 Javascript 為例 (Grokking Simplicity: Taming Complex Software with Functional Thinking)-preview-3
  • 簡約的軟體開發思維:用 Functional Programming 重構程式 - 以 Javascript 為例 (Grokking Simplicity: Taming Complex Software with Functional Thinking)-preview-4
  • 簡約的軟體開發思維:用 Functional Programming 重構程式 - 以 Javascript 為例 (Grokking Simplicity: Taming Complex Software with Functional Thinking)-preview-5
  • 簡約的軟體開發思維:用 Functional Programming 重構程式 - 以 Javascript 為例 (Grokking Simplicity: Taming Complex Software with Functional Thinking)-preview-6
  • 簡約的軟體開發思維:用 Functional Programming 重構程式 - 以 Javascript 為例 (Grokking Simplicity: Taming Complex Software with Functional Thinking)-preview-7
  • 簡約的軟體開發思維:用 Functional Programming 重構程式 - 以 Javascript 為例 (Grokking Simplicity: Taming Complex Software with Functional Thinking)-preview-8
  • 簡約的軟體開發思維:用 Functional Programming 重構程式 - 以 Javascript 為例 (Grokking Simplicity: Taming Complex Software with Functional Thinking)-preview-9
  • 簡約的軟體開發思維:用 Functional Programming 重構程式 - 以 Javascript 為例 (Grokking Simplicity: Taming Complex Software with Functional Thinking)-preview-10
  • 簡約的軟體開發思維:用 Functional Programming 重構程式 - 以 Javascript 為例 (Grokking Simplicity: Taming Complex Software with Functional Thinking)-preview-11
  • 簡約的軟體開發思維:用 Functional Programming 重構程式 - 以 Javascript 為例 (Grokking Simplicity: Taming Complex Software with Functional Thinking)-preview-12
  • 簡約的軟體開發思維:用 Functional Programming 重構程式 - 以 Javascript 為例 (Grokking Simplicity: Taming Complex Software with Functional Thinking)-preview-13
  • 簡約的軟體開發思維:用 Functional Programming 重構程式 - 以 Javascript 為例 (Grokking Simplicity: Taming Complex Software with Functional Thinking)-preview-14
簡約的軟體開發思維:用 Functional Programming 重構程式 - 以 Javascript 為例 (Grokking Simplicity: Taming Complex Software with Functional Thinking)-preview-1

買這商品的人也買了...

商品描述

內容介紹:

Functional programming (函數式程式設計) 是一種已有六十年歷史的程式設計範式,為何現在變得越來越重要?這是因為隨著各種連網設備 (如行動裝置) 和雲端服務的普及,多程序透過網路交換訊息已成為常態,因此分散式系統的開發變得至關重要。

分散式系統軟體開發的複雜性往往導致開發過程容易陷入混亂,程式碼盤根錯節難以除錯維護與擴展,成為揮之不去卻又不敢動的痛處。而 functional programming 的簡約風格因此受到廣泛關注。這種設計範式強調使用函式來構建應用程式,程式碼清晰又容易維護,亦能解決分散式系統多執行緒的問題。除了專門的函數式語言之外,Python、Java、JavaScript、C#、C++、Go 和 Rust 等知名語言的新版中,也因應趨勢增強了對 functional programming 的支援。只要建立了這種思維方式,你就可以應用在自己的程式語言中。

市面上與 functional programming 相關的書籍 (主要為英文書) 通常由學者撰寫,內容偏重理論,而在大型實務專案經驗上顯得不足。這類書中的範例大多過於簡單,未能涉及實際軟體架構問題。與此相反,本書作者擁有二十年在產業界實際運用 functional programming 的經驗,從產業角度出發,旨在幫助那些在軟體工程實踐中遇到困難的工程師重新思考。即使你的目的不是分散式系統,學習 functional programming 的思維都能夠幫助你在撰寫任何程式時想得更周到。

由於 functional programming 的特點 — 無 side effects(額外作用)和資料的不可變性 — 使得它非常適用於開發網路程式,在這種環境中,資料一致性和狀態管理是關鍵挑戰。本書透過 concurrency primitives(併發基本工具)實作、繪製時間線圖、分析並解決併發操作中的插緒問題,有效處理狀態變化和 bug,從而構建可靠的分散式系統程式。

本書特色:

●作者從產業界的視角出發,旨在幫助那些陷入困境的軟體工程師,重新審視並採納 functional programming 程式設計方法。
●為了更貼近軟體工程師的實際狀況,書中以廣泛熟知的 JavaScript 語言做示範。雖然 JavaScript 不是最理想的函數式語言,但這一點反而使其成為一個極佳的教學工具,幫助讀者學會如何重構現有的程式碼。
●大量使用圖表輔助說明,並對程式碼詳細標註,搭配電商平台技術的劇情以增強閱讀和理解效果。
●每章都包括問答練習、重點整理、想想看等互動元素,還有各種實用的提示,以鞏固學習成果並激發深入思考。

作者簡介

作者簡介:

[作者簡介]
Eric Normand 是經驗老道的 functional programming 工程師、教學者與講者,且寫過許多 functional programming 文章。他來自美國紐奧良,於 2000 年開始接觸 Lisp 程式設計,並透過 PurelyFunctional.tv 提供 Clojure 培訓材料。他也從事諮詢服務,幫助企業利用 functional programming 技術實現業務目標。他經常在國際級的程式設計會議上演講,其著作、演講內容、培訓資料和諮詢服務皆可在 LispCast.com 上找到。

[譯者簡介]
黃駿 於國立臺灣大學腦與心智科學研究所碩士班畢業後,曾擔任過行銷、產品設計等工作。有 Java 與 Python 程式語言基礎,對於科學與科技議題抱有高度興趣,隨後投入翻譯工作,目前譯有《無限的力量》、《深度強化式學習》、《深度學習的 16 堂課》、《核心開發者親授!PyTorch 深度學習攻略》、《Excel 儀表板與圖表設計 + Power BI 資料處理》、《AI 必須!從做中學貝氏統計》等,同時經營自己的英文部落格:Neurozo Innovation Blog。

目錄大綱

目錄:

第零篇 函數式思維起手式

第 1 章 初識函數式程式概念
1.1 什麼是函數式程式設計?
1.2 FP 經典定義在實務中的問題
1.3 誤導人的 FP 定義
1.4 本書將函數式程式設計視為一套特定的技術與概念
1.5 區分 Actions、Calculations 與 Data
1.6 函數式程式設計師特別關心會受呼叫影響的程式碼
1.7 函數式程式設計師會區分資料和可執行的程式碼
1.8 函數式程式設計師眼中的 Actions、Calculations 與 Data
1.9 FP 中三類程式碼的特色整理
1.10 區分 Actions、Calculations 與 Data 的好處為何?
1.11 本書與其它 FP 書籍有何不同?
1.12 到底什麼是函數式思維?
1.13 本書會傳授什麼樣的概念與技術?

第 2 章 實務中的函數式思維
2.1 歡迎光臨唐妮的比薩店
2.2 區分 Actions、Calculations 與 Data
2.3 初探分層設計,依『變化頻率』整理程式碼
2.4 使用頭等抽象化
2.5 以時間線圖將分散式系統視覺化
2.6 多條時間線的執行順序可能不同
2.7 關於分散式系統的寶貴經驗
2.8 時間線分界:讓機器人互相等待
2.9 我們從時間線中學到的事:協調多台機器人

第一篇 徹底學通 Actions、Calculations 與 Data

第 3 章 分辨 Actions、Calculations 與 Data
3.1 ACD 的特性與應用時機
3.2 生活中的 ACD
3.3 買菜教會我們的事情
3.4 用函數式思維撰寫程式
3.5 畫出優惠碼電子報的流程圖
3.6 實作優惠券電子報流程
3.7 將函數式思維應用在既存的程式碼
3.8 Actions 會在程式中擴散
3.9 Actions 的形式多變

第 4 章 擷取 Actions 函式中的 Calculations
4.1 歡迎來到 MegaMart.com!
4.2 計算免運費項目
4.3 計算稅金
4.4 程式的可測試性有待提升
4.5 程式的可重複使用性也需提升
4.6 區分 Actions、Calculations 與 Data
4.7 函式有輸入與輸出
4.8 測試與重複使用性和輸入╱輸出相關
4.9 從一個 Action 中擷取出 Calculation
4.10 擷取另一個 Action 中的 Calculation
4.11 查看完整程式碼

第 5 章 改良 Actions 的設計
5.1 應配合需求設計程式
5.2 應依照需求撰寫函式
5.3 設計原則 1:最小化隱性輸入與輸出
5.4 減少隱性輸入與輸出
5.5 快速清理一下程式碼
5.6 替 Calculations 分類
5.7 設計原則 2:『拆解』是設計的本質
5.8 藉由拆解 add_item() 來改良程式
5.9 擷取『寫入時複製』模式
5.10 使用 add_item()
5.11 再次替 Calculations 分類
5.12 更小的函式與更多 Calculations

第 6 章 在變動的程式中讓資料保持不變
6.1 任何操作中的資料都能具有不變性嗎?
6.2 將操作分為『讀取』、『寫入』與『讀取兼寫入』
6.3 實作『寫入時複製』的三步驟
6.4 利用『寫入時複製』將『寫入』變成『讀取』
6.5 對比實作『寫入時複製』前後的程式碼
6.6 將實作『寫入時複製』的操作普適化
6.7 簡介 JavaScript 陣列
6.8 如果操作既是『讀取』也是『寫入』怎麼辦?
6.9 拆解同時『讀取』與『寫入』的函式
6.10 讓一個函式傳回兩個值
6.11 讀取不可變資料結構屬於 Calculations
6.12 程式中包含隨時間而變的狀態
6.13 不可變資料的效率已經夠高
6.14 作用在物件上的寫入時複製操作
6.15 簡介 JavaScript 物件
6.16 將巢狀資料的『寫入』轉換成『讀取』
6.17 巢狀資料中的哪些東西需要複製?
6.18 將『淺拷貝』與『結構共享』視覺化

第 7 章 讓不變性不受外來程式破壞
7.1 使用既有程式 (legacy code) 時的不變性
7.2 寫入時複製函式需與未實作不變性的函式互動
7.3 防禦型複製能守護資料不變性
7.4 實作防禦型複製
7.5 防禦型複製的原則
7.6 將不受信任的程式包裝起來
7.7 你或許看過的防禦型複製
7.8 比較『寫入時複製』與『防禦型複製』
7.9 深拷貝所需資源較淺拷貝高
7.10 以 JavaScript 實作深拷貝很困難
7.11 想像『寫入時複製』與『防禦型複製』之間的對話

第 8 章 分層設計 (1)
8.1 何謂軟體設計?
8.2 何謂分層設計?
8.3 建立設計直覺
8.4 分層設計的原則
8.5 原則 1:讓實作更直觀
8.6 三個不同的檢視等級
8.7 擷取 for loop
8.8 總結 — 原則 1:讓實作更直觀

第 9 章 分層設計 (2)
9.1 複習分層設計的原則
9.2 原則 2:以抽象屏障輔助實作
9.3 抽象屏障可隱藏實作細節
9.4 細節忽略是雙向的
9.5 更改『購物車』的資料結構
9.6 將『購物車』重新實作為物件
9.7 抽象屏障讓我們能夠忽略細節
9.8 何時該 (或不該) 用抽象屏障?
9.9 總結 — 原則 2:以抽象屏障輔助實作
9.10 程式變得更清楚了!
9.11 原則 3:讓下層函式保持簡約與不變
9.12 總結 — 原則 3:讓下層函式保持簡約與不變
9.13 原則 4:分層只要舒適即可
9.14 呼叫圖呈現了哪些與程式有關的資訊?
9.15 修改呼叫圖上層的函式較安全
9.16 測試底層函式較重要
9.17 底層函式較能重複利用
9.18 總結 — 呼叫圖告訴我們的訊息

第二篇 頭等抽象化

第 10 章 頭等函式 (1)
10.1 行銷部門仍需與開發小組協調
10.2 程式碼異味:函式名稱中的隱性引數
10.3 重構 1:將隱性引數轉換為顯性參數
10.4 辨識頭等與非頭等
10.5 用字串當屬性名稱會不會增加錯誤發生率?
10.6 將屬性名稱頭等化,會不會造成 API 難以修改?
10.7 為什麼要用物件實作資料?
10.8 頭等函式可取代任何語法
10.9 For 迴圈重構範例
10.10 重構 2:以回呼取代主體實作
10.11 內嵌與匿名函式
10.12 為什麼要將 saveUserData() 包裹在函式中?

第 11 章 頭等函式 (2)
11.1 函式名稱中的隱性引數與兩種重構
11.2 重構寫入時複製
11.3 重構陣列的寫入時複製
11.4 讓函式傳回函式

第 12 章 利用函式走訪
12.1 函式名稱中的隱性引數與兩種重構
12.2 MegaMart 想建立新的電子郵件系統
12.3 從範例函式中擷取 map() 的實作
12.4 三大函數式工具 — map() 
12.5 傳入函式的三種方法
12.6 範例:取得所有顧客的電子郵件地址
12.7 從範例函式中擷取 filter() 的實作
12.8 三大函數式工具 — filter()
12.9 範例:找出從未消費過的顧客
12.10 從範例函式中擷取 reduce() 的實作
12.11 三大函數式工具 — reduce()
12.12 範例:連接字串
12.13 reduce() 可以做什麼?
12.14 比較三大函數式工具

第 13 章 串連函數式工具
13.1 新的資料請求
13.2 函式鏈整理方法 (1) — 為步驟命名
13.3 函式鏈整理方法 (2) — 為回呼函式命名
13.4 比較兩種函式鏈整理方法
13.5 範例:寄送電子郵件給僅消費過一次的顧客
13.6 以函數式工具重構既有的 for 迴圈
13.7 訣竅 1:將資料儲存至獨立陣列
13.8 訣竅 2:細化步驟
13.9 比較巢狀迴圈與函式鏈寫法
13.10 總結撰寫函式鏈的訣竅
13.11 替函式鏈除錯的訣竅
13.12 其它函數式工具
13.13 以 reduce() 建立資料
13.14 擴增原本的資料
13.15 將 method chaining 中的『點』對齊

第 14 章 處理巢狀資料的函數式工具
14.1 用高階函式處理物件內的值
14.2 讓屬性名稱變顯性
14.3 實作更新物件內屬性值的 update()
14.4 以 update() 修改物件屬性值
14.5 重構 3:以 update() 取代『取得、修改、設定』
14.6 函數式工具 — update()
14.7 將 update() 的行為視覺化
14.8 將巢狀資料的 update() 視覺化
14.9 用 update() 處理巢狀資料
14.10 實作成普適化的 updateOption()
14.11 實作兩層巢狀結構的 update2()
14.12 視覺化說明 update2() 如何操作巢狀物件
14.13 函式 incrementSizeByName() 的四種實作方法
14.14 實作三層巢狀結構的 update3()
14.15 實作任意巢狀深度的 nestedUpdate()
14.16 安全的遞迴需具備什麼?
14.17 將 nestedUpdate() 的行為視覺化
14.18 比較遞迴和迴圈
14.19 遇到深度巢狀資料時的設計考量
14.20 為巢狀資料建立抽象屏障
14.21 總結高階函式的應用

第 15 章 解析時間線
15.1 發現 bug!
15.2 連續點兩下滑鼠
15.3 用時間線圖呈現時間上的變化
15.4 畫時間線圖需掌握兩項基本原則
15.5 Actions 執行順序的兩項細節
15.6 畫出放入購物車的時間線圖:步驟 1
15.7 非同步回呼要畫在不同時間線上
15.8 不同程式語言採用不同執行緒模型
15.9 一步步建立時間線
15.10 畫出『放入購物車』的時間線圖:步驟 2
15.11 時間線圖能反映兩類序列式程式
15.12 時間線圖能反映平行程式碼的順序不確定性
15.13 改善時間線的原則
15.14 JavaScript 的單執行緒
15.15 JavaScript 的非同步佇列
15.16 AJAX 請求與回應
15.17 AJAX 非同步處理的完整流程
15.18 簡化時間線
15.19 閱讀完成的時間線圖
15.20 簡化『放入購物車』的時間線圖:步驟3
15.21 複習時間線圖製作流程
15.22 總結繪製時間線圖的技巧
15.23 並列時間線圖能突顯出問題
15.24 『慢慢點兩次滑鼠』必產生正確結果
15.25 『快速連點兩次滑鼠』可能產生錯誤結果
15.26 共享資源的時間線是問題所在
15.27 將全域變數轉換成區域變數
15.28 將全域變數轉換成引數
15.29 增加函式的可重複使用性
15.30 在非同步呼叫中,需利用回呼實現顯性輸出

第 16 章 多條時間線共享資源
16.1 改善時間線的原則
16.2 『放入購物車』程式仍可能出錯
16.3 DOM 更新的順序必須固定
16.4 在JavaScript 中自行建立佇列
16.5 從現實生活中的共享案例獲取靈感
16.6 讓佇列可重複使用
16.7 分析時間線
16.8 利用時間線圖找出錯誤
16.9 設定佇列容量上限

第 17 章 協調時間線
17.1 改善時間線的原則
17.2 發現新 bug
17.3 優化小組到底改了什麼
17.4 繪製時間線圖步驟 1:辨識 Actions
17.5 繪製時間線圖步驟 2:將 Actions 畫在時間線上
17.6 繪製時間線圖步驟 3:簡化時間線圖
17.7 分析潛在順序
17.8 優化後的程式為何較快?
17.9 等待兩個平行處理的回呼
17.10 實現時間線分界的 concurrency primitives
17.11 在『放入購物車』程式裡應用 Cut()
17.12 再次分析潛在順序
17.13 分析平行處理的時間
17.14 對『多次點擊』進行分析
17.15 讓 Action 只能執行一次 primitive
17.16 隱性 vs. 顯性時間模型
17.17 總結:操作時間線

第 18 章 反應式與洋蔥式架構
18.1 兩種獨立的設計架構
18.2 與程式更動有關的因與果糾纏不清
18.3 什麼是反應式架構?
18.4 反應式架構是雙面刃
18.5 頭等狀態模型 — Cell
18.6 把 ValueCell 變成反應式
18.7 如何在 cell primitive 改變時更新運費標籤
18.8 用 FormulaCell 處理從其它變數計算來的值
18.9 FP 中的可變狀態
18.10 反應式架構的三大效果
18.11 切斷變更操作與顯示結果之間的關聯
18.12 將連續步驟轉換成處理管道
18.13 讓時間線更具彈性
18.14 複習:兩種獨立的設計架構
18.15 什麼是洋蔥式架構?
18.16 複習:Actions、Calculations 與 Data
18.17 複習:分層設計
18.18 傳統的層狀架構
18.19 函數式架構
18.20 提升可修改與可重複使用性
18.21 檢視該操作中包含哪些元素
18.22 考慮程式碼的可讀性、開發速度與效能

第 19 章 踏上函數式設計之途
19.1 本章的內容規劃
19.2 各位已掌握的專業技巧
19.3 不能忘記的三大重點
19.4 新技術的學習曲線
19.5 提升熟練度的方法
19.6 沙盒:開始你的個人專案
19.7 沙盒:找練習題來做
19.8 實務操作:為程式碼除錯
19.9 實務操作:漸近地改善既有設計
19.10 常見的 FP 程式語言
19.11 最多工作機會的 FP 語言
19.12 適合在什麼平台上運行
19.13 重點 FP 特徵
19.14 函數式設計的數學基礎
19.15 進階閱讀