[IT] Clean Architecture - 第4章 - 結構化程式設計

ch4. 結構化程式設計 Edsger Wybe Dijkstra 於 1930 年出生在鹿特丹。他在第二次世界大戰期間倖存於鹿特丹的轟炸,以及德國對荷蘭的占領,並於1948年以數學、物理、化學、生物最高分從高中畢業。1952年3月,21歲的 Dijkstra 在阿姆斯特丹的數學中心找到工作,成為荷蘭第一位程式設計師。 1955年,已經當了三年的程式設計師,且同時身為學生的 Dijkstra 得出一個結論:程式設計的智力挑戰比起理論物理學還大。因此,他選擇了程式設計作為他的長期職業。 1957年,Dijkstra 與 Maria Debets 結婚。當時,在荷蘭結婚必須登記職業,荷蘭的公家機關並不接受 programmer 這個職業,他們從未聽過這樣的職業,為了滿足他們,Dijkstra 在職業欄中將自己定位為「理論物理學家」。 Dijkstra 與他的老闆 Adriaan van Wijngaarden 討論著以程式設計當作他的生涯志向,他認為沒有人會將程式設計視為一門學科或是科學,因此他認為自己可能不會被認真以待,然而他的老闆回答說,Dijkstra 將有可能成為那位發現新學科,以致於將軟體變成一門科學的人。 Dijkstra 在真空管時代開始了他的職業生涯,當時的計算機很巨大、脆弱、緩慢、不可靠、且極其有限。在早期,程式是二進制或是非常粗糙的組合語言編寫的,並以紙帶或打孔卡片作為輸入的這種物理形式存在,編輯/編譯/測試的循環就需要數小時甚至數天的時間。 正是在這個原始的環境造就了 Dijkstra 做出了他偉的的發現。 證明 Dijkstra 很早就發現,程式設計是一件難事,且程式設計師也不容易將它做好。任何複雜的程式都包含了太多人類大腦在沒有幫助下可以管理的細節。忽視一個極小的細節程式看似可以運作正常,但卻可以以出人意料的方式失敗。 Dijkstra 的解決方案是應用數學的證明法。他的願景是建立一個如同歐基里得的公理、定理、推論和引理的層次結構。Dijkstra 認為程式設計師可以像數學家一樣使用這樣的證明方法,換句話說,程式設計師應該要運用這些經過驗證的結構,並將它們與自己證明正確的程式碼相結合。 當然,為了使這一切開始進行,Dijkstra 意識到他必須要撰寫一些範例以展示如何用基本的證明方法來證明簡單的演算法,而他發現這是一件極具挑戰性的事。 在調查的過程中,Dijkstra 發現某些使用 goto 語句的情況會阻止模塊被遞迴地分解成更小的單元,從而阻礙了使用分而治之的方法進行證明的可能性。 然而,goto 的其他用途卻沒有這個問題。Dijkstra 意識到,「好的」goto 使用方法對應到簡單的選擇和迭代控制結構,例如 if/then/else 和 do/while。只使用這些控制結構的模塊才可以被遞迴的切割成可以被證明的單元。 Dijkstra 認識到,當這些控制結構與順序執行相結合是特殊的。在當時的兩年前,已經由 Böhm 和 Jacopini 證明了,所有程式都可以由三個結構建構而成:順序(sequence)、選擇(selection)、迭代(iteration)。 這個發現非常了不起:使一個模塊可證明的控制結構,正是構成所有程式的最小控制結構集合。因此,結構化程式設計應運而生。 Dijkstra 證明了順序語句可以通過簡單的列舉來證明其正確性。這種技術利用數學方法系統性地追蹤語句的輸入與輸出,與一般的數學證明無異。 Dijkstra 通過重新應用列舉法來解決選擇問題,對於選擇中的每條路徑都進行了列舉,如果所有的路徑都產生了適當的數學結果,那麼就表示證明是可靠的。 迭代則有些不同,為了證明迭代的正確性,Dijkstra 必須使用歸納法。他通過列舉證明了 1 得情況,然後他再次通過列舉證明了如果假設 N 是正確的,則 N+1 也是正確的,他也證明了迭代的起始條件與結束條件的正確性。...

<span title='2023-10-03 22:28:44 +0800 +0800'>October 3, 2023</span>&nbsp;·&nbsp;1 min&nbsp;·&nbsp;Rain Hu

[IT] Clean Architecture

乾淨架構 設計(design)與架構(architecture)為何重要? 軟體架構的目標是將開發與維護軟體系統所需的人力最小化。 不好的設計會使維護的成本愈來愈高。 每次版本的發布時的生產力。 良好的開發模式(TDD)大幅減少開發時間。 行為(behavior)與架構(architecture) 行為:緊迫但並非特別重要 架構:重要不緊迫 ∵ 緊急且重要 > 不緊急但重要 > 緊急但不重要 > 不緊急且不重要 ∴ 大多情況下,架構(設計)比行為(開發)更重要。 程式設計範式(paradigms) 結構化程式設計(structed programming) 不要使用 goto,使用結構化的設計模式。(順序、選擇、迭代) 總結:對直接控制權施加限制。 關注點:功能 物件導向程式設計(object-oriented programming) 使用多型來避免函數指針的濫用。 總結:對間接控制權施加限制。 關注點:組件分離 函式程式設計(functional programming) λ演算的概念是不可變性,符號的值不會改變,意味著沒有賦值。 總結:對賦值施加限制。 關注點:數據管理 物件導向設計: 依賴反轉: 商業邏輯不依賴於 UI 與 DB,UI 與 DB 可以做為商業邏輯的插件。 小結: 三種範式都在約束你寫 code 的某些行為。這些約束就是在制定規則。 SOLID 設計原則 SRP: 單一職責原則(The Single Responsibility Principle) 一個模組只有一個原因(用戶/利益相關者)需要改變。 OCP: 開放封閉原則(The Open-Closed Principle) 軟體工程應對擴展開放,但對修改封閉。 LSP: 里氏替體原則(The Liskov Substitution Principle) 避免簡單的可替代性違規導致大量的額外機制。 ISP: 介面隔離原則(The Interface Segregation Principle) 關注點分離。將一個多功能的物件拆成繼承三個不同功能介面的物件。 DIP: 依賴反轉原則(The Dependency Inversion Principle) 組件原則 組件是部署的單位,他們是系統的最小單元。 在 Java,他們是 jar 檔。 在 Ruby,他們是 gem 檔。 在 ....

<span title='2023-09-29 02:03:47 +0800 +0800'>September 29, 2023</span>&nbsp;·&nbsp;2 min&nbsp;·&nbsp;Rain Hu

[IT] Clean Architecture - 重點整理

乾淨架構(Clean Architecture) 筆記 分層 乾淨架構中從外而內依序為 Framework Layer Interface Adapter Layer Application Layer Domain Layer Models 一般來說會有四個 Models View Model(給前端) App Model(App Layer 隔離 Domain Layer 所用,aka DTO) Domain Model Data Model(for DBMS) Usecase App Layer 中的 Usecase 做四件事: 查 改 存 推 單向依賴原則 依賴的方向必為單向且為 \(\boxed{\text{Interface Adapter}} \rightarrow \boxed{\text{Application Layer}} \rightarrow \boxed{\text{Domain Layer}}\) Repository Application Layer 為了遵守單向依賴,與 ORM 解耦會做一次依賴反轉,翠取 Repository 介面。 套用乾淨架構的效益衡量 Model Mapping 的成本 vs. 獨立出「領域模型」的價值 省下更換技術的成本(migration cost) 「領域層」的部分通常會結合 DDD

<span title='2023-09-23 20:03:55 +0800 +0800'>September 23, 2023</span>&nbsp;·&nbsp;1 min&nbsp;·&nbsp;Rain Hu

[IT] Clean Architecture - 第3章 - 程式設計範式總覽

ch3. 程式設計範式總覽 這個概述章節中包含的三種範式(paradigm)是結構化編程、物件導向編程和函數式編程。 結構化程式設計(Structured Programming) 第一個被採用的範式(但不是第一個發明的)是結構化程式設計,由艾德斯格·韋伯·迪科斯特拉(Edsger Wybe Dijkstra)在1968年發現。迪科斯特拉指出,無節制的跳躍(goto語句)對程式結構是有害的。正如我們在接下來的章節中將看到的那樣,他用更為熟悉的if/then/else和do/while/until結構取代了這些跳躍。 一句話總結結構化程式設計: 結構化程式設計對直接控制權的轉移施加限制 Structured programming imposes discipline on direct transfer of control. 物件導向程式設計(Object-Oriented Programming) 第二個採用的範式實際上是在1966年早兩年被奧利·約翰·達爾(Ole Johan Dahl)和克利斯登·奈加特(Kristen Nygaard)發現的。這兩位程式設計師注意到,在ALGOL語言中,函數呼叫的 stack frame 可以移動到 heap,從而使函數聲明的區域變數在函數返回後仍然存在。該函數成為一個類的構造函數,區域變數成為實例變數,嵌套函數則成為方法。這不可避免地導致了多態的發明,用以限制函數指針的使用。 一句話總結物件導向程式設計: 物件導向程式設計對間接控制權的轉移施加限制 Object-oriented programming imposes discipline on indirect transfer of control. 函數式程式設計 第三種範式,最近才開始被採用,卻是最早被發明的。事實上,它的發明早於程式設計本身。函數式程式設計是阿隆佐·邱奇(Alonzo Church)的工作的直接產物,他在1936年時發明了λ演算法(l-calculus),當時圖靈也在研究同樣的問題。他的λ演算法是基於9158年由約翰·麥卡錫(John McCarthy)發明的LISP語言,λ演算法有一個最基本的概念是不可變性(immutability),也就是說,變數的值不會改變。這意味著函數式程式設計並不會有賦值的敘述。事實上,大多數的函數式程式語言,有自己的方法去改變變數的值,但只有在非常嚴格的限制下可以使用。 一句話總結函數式程式設計: 函數式程式設計對賦值施加限制 Functional programming imposes discipline upon assignment 討論 注意到本章所介紹到的三個範式,都是在限制程式設計師的能力,而非增加新的能力。每個範式都在告訴我們什麼不應該做,而不是應該做什麼。 從另一角度來看,從結構化程式設計消除了go to語句,從物件導向程式設計消除了function pointers,從函數式程式設計消除了assignment。我們還有什麼可以消除的呢? 答案很可能是沒有。因此這三種範式很有可能是唯一的三種,至少是唯三限制型的範式,另一個證據是,在爾後的數十年間,也沒有再出現任何的範式。 結論 從範式的歷史,我們可以怎麼與架構做聯想呢? 1. 我們利用多型的機制來跨越架構的邊界。 2. 我們利用函數式程式設計來約束對數據的位置與訪問權限。 3. 我們利用結構化程式設計作為模塊的演算法基礎。 注意這三個與建築的三個重要關注點不謀而合:功能、組件分離、數據管理。

<span title='2023-09-07 22:07:54 +0800 +0800'>September 7, 2023</span>&nbsp;·&nbsp;1 min&nbsp;·&nbsp;Rain Hu

[IT] Clean Architecture - 第二部分 從基礎構件開始: 程式設計範式(Paradigms)

軟體架構始於程式碼,因此我們將從程式碼的角度開始討論架構,看看自從程式碼被寫下以來我們所學到的內容。 1938年,艾倫·圖靈(Alan Turing)奠定了計算機編程的基礎。他並不是第一個構想可編程機器的人,但他是第一個理解程式即數據(programs are simply data)的人。到了1945年,圖靈已經在真正的電腦上用我們現在能夠認出的程式碼編寫真正的程式了。這些程式使用了循環(loops)、分支(branches)、賦值(assignment)、子程序(subroutines)、堆棧(stacks)和其他熟悉的結構。但,圖靈的語言是二進制的。 自從那些日子以來,程式設計界發生了許多革命。其中一個我們都非常熟悉的革命就是語言的革命。首先,在1940年代末期,出現了組合語言(assemblers)。這些「語言」解放了程式設計師將他們的程式轉換成二進制的苦差。1951年,格雷斯·霍珀(Grace Hopper)發明了第一個編譯器 A0。事實上,她創造了「編譯器(compiler)」這個詞彙。Fortran 在1953年被發明出來。接著,一股源源不斷的新程式語言湧入 - COBOL、PL/1、SNOBOL、C、Pascal、C++、Java等等,無窮無盡。 另一個可能更重要的革命是在程式設計範式方面。範式是編程的方式,與語言相對無關。範式指導了開發人員應該使用哪些程式結構,以及何時使用它們。 迄今為止,已經有三種這樣的範式,也不太可能再有其它的範式,原因後述。

<span title='2023-09-05 21:43:11 +0800 +0800'>September 5, 2023</span>&nbsp;·&nbsp;1 min&nbsp;·&nbsp;Rain Hu