[IT] 程式語言的演進 Programming Language Evolution

程式設計語言本身並非架構的產物,但如果我不寫關於它,我會覺得《軟體架構編年史》似乎缺少了些什麼。 那麼,讓我們快速回顧一下程式語言的歷史,它的演變,並檢查我們可以從中學到什麼。我在文章中加入了一些日期,僅作為參考,它們應被視為大致的估計,重要的是演變的順序和他們試圖解決的問題。 1950s - Non-structured Programming Assembly ~1951 軟體開發曾是一項非常晦澀的活動,只在世界上少數地方進行。當時流行的語言是組合語言,它使用了非常低階的操作,如 add, sub, goto,並直接操作記憶體地址。建立一個簡單的應用程式既緩慢又困難。要創建一個簡單的 if 語句,我們需要幾行程式碼,對於一個迴圈,則需要超過幾行的程式碼…稍後才出現了將程式碼分組和重用的可能性,所以當時的編碼風格非常線性,程式碼的重用僅限於在檔案內或檔案間複製和貼上程式碼。 1960s – Structured Programming Algol ~1958, Fortran 結構化程式設計出現了,引入了 code blocks 概念,控制結構 if, then, else, case, for, while, do, …,和子程序的構造。然後,我們可以創建更有趣的程式流程,更重要的是,我們可以將程式碼指令分組並重用它,儘管有一些限制,例如子程序總是對同一全域變數進行操作。但就在這個時候,重用性 (reusability) 的概念開始被使用。 1970s – Procedural & Functional Programming Pascal ~1970, C ~1972 程序和函數式程式設計在1970年代開始活躍起來。到了這個時候,我們終於有了: 程序(Procedures):一組不返回數據的指令 函式(Functions):一組會回傳資料的指令集 資料結構(Data structures):記錄,類似於關聯式陣列 模組(Modules):可以被導入到其他程式碼檔案的程式碼檔案。 在1970年代,「Spaghetti code」這個詞也被創造出來,這是在Edsger W. Dijkstra於1968年寫給「計算機機構通訊」(CACM)的信中提出的,該信的標題為「Go To Statement Considered Harmful」。 在1970年代晚期,事件導向程式設計(Event Oriented Programming)的初步概念首次浮現,而Trygve Reenskaug則撰寫了他關於MVC(使用事件)的論文。 有了這些改進,因此我們有更好的重用性(reusability),因為子程序(程序和函式),我們可以使用不同的數據執行相同的邏輯。我們也可以通過將相關數據分組到複雜的數據結構中來模擬領域概念(domain concepts)。最後,我們在**解耦(decoupling)和模組化(modularity)**方面邁出了第一步,我們可以創建在其他程式碼文件和事件中可重用的程式碼,以將客戶端程式碼與正在執行的邏輯解耦。 1980s – Object Oriented Programming Simula ~1965, Smalltalk-71 ~1971, C++ ~1980, Erlang ~1986, Perl ~1987, Python ~1991, Ruby ~1993, Delphi, Java, Javascript, PHP ~1995 ...

October 14, 2023 · 1 分鐘 · hgraca

[IT] 軟體架構前提 The Software Architecture Premises

在這篇文章中,我將建立有關軟體架構的最初概念,這將有助於更好地理解接下來的文章。 沒有萬靈丹 無論你如何理解我在《軟體架構編年史》中所談論的內容,最重要的是要明白,並不存在萬能的解決方案。儘可能多學習不同的方法,理解每種方法的優點和缺點,以及它們解決的具體技術問題。 然後,在接受新的挑戰時,首先要理解業務和終端用戶的需求。只有在清楚理解這些需求之後,您才能理智地思考應該使用哪種架構風格和模式來更好地解決手頭的問題。 最後,做出你自己的選擇,也許實施其中一種已知的解決方案,或者創建你自己的設計來適應你特定的問題。 Some architectural styles are often portrayed as ‘silver bullet’ solutions for all forms of software. However, a good designer should select a style that matches the needs of the particular problem being solved. - Roy Fielding, 2000 有些架構風格常被描繪為所有軟體問題的「萬靈丹」解決方案。然而,一個優秀的設計師應該選擇一種與特定問題需求相匹配的風格。 - 羅伊·菲爾丁,2000 Terminology 術語 在軟體開發的世界裡,使用的術語充滿了模糊性,因此,在繼續之前,澄清我所使用的一些術語的意義是非常重要的。 Functional 功能性的 這是任何一段程式碼、方法、類別、類別群組,它在應用程式中擔任純粹的技術角色。它與領域無關,僅代表應用程式中的技術能力。例如: Layers Factories Repositories Value Objects Views ViewModels Conceptual 概念性的 這是任何一段代碼、方法、類別、一組類別,都反映了應用程式中的領域概念。它與領域直接相關,代表了應用程式中的業務能力。例如: User Product Stock Management Product Variants Checkout Upsells 這種分離並不意味著一個程式碼單元不能以兩種方式(功能性和概念性)被引用。例如,一個 Money 類別可以代表一個 domain concept,也可以是一個 value object。 ...

October 14, 2023 · 1 分鐘 · hgraca

[IT] 軟體架構編年史 The Software Architecture Chronicles

前言:這系列文章為翻譯自作者 hgraca。 這篇文章是關於軟體架構系列文章的第一篇。在這些文章中,我將分享我所學習到的軟體架構知識,我如何看待它,以及我如何運用這些知識。 我這一系列的文章稱為「軟體架構編年史」,並非因為我自認為是一位偉大的作家,而是因為我覺得這個名字有點俗氣,又帶點趣味。 在這篇首篇文章中,我將會談論我為何要撰寫這一系列的文章,以及接下來將會有什麼內容。 認識歷史的重要性 Those who fail to learn History are doomed to repeat it. - George Santayana, The Life of Reason, 1905 未能學習歷史的人注定要重蹈覆轍。 - 喬治‧桑塔亞納《理性的生活》,1905 我認為從歷史中學習是非常重要的,它能教導我們事情。在個人層面上,我們終究需要(也希望)能從錯誤中學習。從國家的角度來看,歷史幫助塑造我們的文化、創造群體的概念,因此有了「台灣人」的觀念,一種國家認同。同時,歷史也幫助我們從祖先的錯誤中學習,比如信仰那些有著怪異思想的人,如二戰… 對程式開發者而言,歷史有助於我們仰靠前人的經驗上,少走許多錯的道路,並讓我們「站在巨人的肩膀上」達到更高的境界! 在我成為更好的開發者路上,我閱讀了很多文章,觀看了許多演講,我盡我所能的站在巨人的肩膀上。 有一件事情使我感到困惑,那就是許多意見是基於意見之上的意見…這就像是以訛傳訛,我們最終得到是對於一篇論文、文章或書籍真正內容的扭曲理解。 因此,我開始在網路上搜尋原始的論文、文章和書籍,這些都是我認為對我的工作最重要的概念,並自己對它們進行思考。 這些文章是這種推理的結果,因為我試圖理解這些概念是如何產生的,以某種程度上的時間順序來看。 撰寫這些文章迫使我大量閱讀和思考所有議題,並幫助我理解當代在軟體開發中使用的技術。我希望這些文章能對更多開發者有所幫助。 然而,如果你讀到一些你不理解或是不認同的內容,請告訴我,我非常願意討論這些議題,並從討論中學習,也願意修正我錯誤的觀點。 文章列表 1. 軟體架構前提 (Software Architecture Premises) 2. 程式語言的演進 (Programming Languages Evolution) 3. 架構風格 / 架構模式 / 設計模式 (Architectural Styles vs. Architectural Patterns vs. Design Patterns) 4. 單體架構 (Monolithic Architecture) 5. 分層架構 (Layered Architecture) ...

October 13, 2023 · 2 分鐘 · hgraca

[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 也是正確的,他也證明了迭代的起始條件與結束條件的正確性。 這樣的證明是費時且複雜的,但它們具備了證明的意義。隨著它們的發展,建立一個歐基里得式階層式的定理看似變得可行。 ...

October 3, 2023 · 1 分鐘 · 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) ...

September 29, 2023 · 2 分鐘 · Rain Hu

[IT] 事件總線 EventBus

EventBus EventBus 用於維護一個事件源與事件處理的映射字典 通過 Singleton,確保 EventBus 的唯一入口 利用反射完成事件源與件事處理的初始化綁定 提供統一的事件注冊(register)、取消注冊(unsubscribe)和觸發(trigger)。 Interfaces IEventData public interface IEventData { DateTime EventTime { get; set; } object EventSource {get; set; } } IEventHandler public interface IEventHandler { } IEventHandler`1 public interface IEventHandler<TEventData> : IEventHandler where TEventData : IEventData { void HandlerEvent(TEventData eventData); } base class EventData public class EventData : IEventData { public DateTime EventTIme { get; set; } object EventSource { get; set; } public EventData() { EventTime = DateTime.Now; } } Domain FishType public enum FishType { None, 鯽魚, 鯉魚, 黑魚, 青魚, 草魚, 鱸魚 } FishingEventData : EventData public class FishingEventData : EventData { public FishType FishType { get; set; } public FishingMan FishingMan { get; set; } } FishingEventHandler : IEventHandler public class FishingEventHandler : IEventHandler<FishingEventData> { public void HandleEvent(FishingEventData eventData) { var type = eventData.FishType; var fishMan = eventData.FishingMan; var Name = fishMan.Name; if (type == FishType.None) { fishMan.Message = string.Format("{0}: 沒有釣到魚, 累計釣了{1}條魚", Name, fishMan.FishCount); } else { fishMan.FishCount++; fishMan.Message = string.Format("{0}: 釣到一條[{2}], 累計釣了{1}條魚", Name, fishMan.FishCount, type); } } } FishingMan public class FishingMan { public string Name { get; set; } public int FishCount { get; set; } public FishingRod FishingRod { get; set; } public string Message { get; set; } public FishingMan(string name) { Name = name; FishCount = 0; } public void Fishing() { FishingRod.ThrowHook(this); } } FishingRod 用反射註冊事件 public class FishingRod { public string Message { get; private set; } public FishingRod() { } public void ThrowHook(FishingMan man) { if (new Random().Next() % 2 == 0) { var type = (FishType)(new Random().Next(0, 5) + 1); Message = ("魚兒上鉤了!"); if (FishingEvent != null) { var eventData = new FishingEventData { FishingMan = man, FishType = type }; EventBus.Default.Trigger<FishingEventData>(eventData); } } else { var type = FishType.None; Message = ("可惜了!"); if (FishingEvent != null) { var eventData = new FishingEventData { FishingMan = man, FishType = type }; EventBus.Default.Trigger<FishingEventData>(eventData); } } } } EventBus 實作 用一個靜態單例來統一管理事件 public class EventBus { public static EventBus Default => new EventBus(); private readonly ConcurrentDictionary<Type, List<Type>> _eventAndHandlerMapping; private EventBus() { _eventAndHandlerMapping = new ConcurrentDictionary<Type, List<Type>>(); MapEventToHandler(); } private void MapEventToHandler() { Assembly assembly = Assembly.GetEntryAssembly(); foreach (var type in assembly.GetTypes()) { if (typeof(IEventHandler).IsAssignableFrom(type)) { Type handlerInterface = type.GetInterface("IEventHandler`1"); if (handlerInterface == null) continue; Type eventDataType = handlerInterface.GetGenericArguments()[0]; if (_eventAndHandlerMapping.ContainsKey(eventDataType)) { List<Type> handlerTypes = _eventAndHandlerMapping[eventDataType]; handlerTypes.Add(type); _eventAndHandlerMapping[eventDataType] = handlerTypes; } else { var handlerTypes = new List<Type> { type }; _eventAndHandlerMapping[eventDataType] = handlerTypes; } } } } public void Register<TEventData>(Type eventHandler) { List<Type> handlerTypes = _eventAndHandlerMapping[typeof(TEventData)]; if (!handlerTypes.Contains(eventHandler)) { handlerTypes.Add(eventHandler); _eventAndHandlerMapping[typeof(TEventData)] = handlerTypes; } } public void Unsubscribe<TEventData>(Type eventHandler) { List<Type> handlerTypes = _eventAndHandlerMapping[typeof(TEventData)]; if (!handlerTypes.Contains(eventHandler)) { handlerTypes.Remove(eventHandler); _eventAndHandlerMapping[typeof(TEventData)] = handlerTypes; } } public void Trigger<TEventData>(TEventData eventData) where TEventData : IEventData { List<Type> handlers = _eventAndHandlerMapping[eventData.GetType()]; if (handlers != null && handlers.Count > 0) { foreach (var handler in handlers) { MethodInfo methodInfo = handler.GetMethod("HandleEvent"); if (methodInfo != null) { object obj = Activator.CreateInstance(handler); methodInfo.Invoke(obj, new object[] { eventData }); } } } } } demo MacOs Cocoa Project public partial class ViewController : NSViewController { public ViewController (IntPtr handle) : base (handle) { } public override void ViewDidLoad () { base.ViewDidLoad (); jeff = new FishingMan("Jeff"); rod = new FishingRod(); jeff.FishingRod = rod; EventBus eventBus = EventBus.Default; eventBus.Register<FishingEventData>(typeof(FishingEventHandler)); } FishingMan jeff; FishingRod rod; public override NSObject RepresentedObject { get { return base.RepresentedObject; } set { base.RepresentedObject = value; } } partial void Button_Click(NSButton sender) { jeff.Fishing(); TextLabel.StringValue = rod.Message; TextLabel2.StringValue = jeff.Message ?? ""; } }

September 25, 2023 · 3 分鐘 · 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

September 23, 2023 · 1 分鐘 · Rain Hu

[IT] LINQ: IQueryable Provider

可重複使用的 IQueryable 基類 IQueryable 簡介 在 C# 最新版本中的 IQueryable 已經不再是一個介面,而是分為兩個部分: IQueryable 與 IQueryProvider。在開始實作之前,我們必須先了解一下這兩個介面。 public interface IQuerable : IEnumerable { Type ElementType { get; } Expression Expression { get; } IQueryProvider Provider { get; } } public interface IQueryable<T> : IEnumerable<T>, IQueryable, IEnumerable { } IQueryable 有三個唯讀屬性: ElementType 代表了元素的類型 (或等於 IQueryable<T> 中的 T) Expression 代表了查詢對應的表達式。這是 IQueryable 存在的核心要素。在 IQueryable 的內部,實際上是一個表示查詢的表達式,它將查詢表示為 LINQ 查詢運算子/方法調用的樹狀結構。如果進一步看,你會發現,IQueryable 或是 Queryable 都只是在提供一個自動構建表達式樹節點 (expression tree nodes) 的機制。當我們對 IQeuryable 使用 Where 方法時,它只是回傳一個新的 IQueryable,並且在進行調用的樹頂添加一個方法表達式樹節點。 Provider 作為真正的「提供者」,它負責原先所有 IQueryable 的執行方法。 IQueryProvider 簡介 public interface IQueryProvider { IQueryable CreateQuery(Expression expression); IQueryable<TElement> CreateQuery<TElement>(Expression expression); object Execute(Expression expression); TResult Execute<TResult>(Expression expression); } 當我們進一步觀察 IQueryProvider,會發現它事實上只有兩個操作:CreateQuery、Execute,只是各有一個泛型與非泛型的方法。一般我們會使用泛型的方法,因為它可以避免使用反射來建構實例,從而提高性能。 ...

September 21, 2023 · 19 分鐘 · 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. 我們利用結構化程式設計作為模塊的演算法基礎。 注意這三個與建築的三個重要關注點不謀而合:功能、組件分離、數據管理。 ...

September 7, 2023 · 1 分鐘 · 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等等,無窮無盡。 另一個可能更重要的革命是在程式設計範式方面。範式是編程的方式,與語言相對無關。範式指導了開發人員應該使用哪些程式結構,以及何時使用它們。 迄今為止,已經有三種這樣的範式,也不太可能再有其它的範式,原因後述。

September 5, 2023 · 1 分鐘 · Rain Hu