OCP
我們先來想一個問題:軟體服務與實體產品最大的差異是什麼?它的價值來自哪裡?
一台車、一棟房子、一支手機,當它製造完成、出廠後,要再改動幾乎不可能。你或許可以維修或加裝零件,但「核心設計」已經固定。因為製造實體產品的成本主要在 生產鏈:模具、材料、工廠產線、物流。只要東西做出來,要大幅修改就得「重新開模 → 重做 → 巨大成本」。因此,實體產品通常必須在設計階段就「盡量定案」,避免後期修改。
而軟體完全不同。軟體的「製造成本」幾乎為零,複製一份就能立刻運行。它的價值不在於「做出來」,而在於是否持續符合需求。當需求變動,只要重新開發、部署,就能立刻改變行為。軟體的主要成本來自 設計與維護,而不是製造。這也是為什麼軟體可以一直改,甚至「應該一直改」──因為它的價值就在於能持續貼合不斷變化的需求。
軟體服務的本質是 資訊結構,而非物理材料。你可以隨時推翻邏輯、重組流程,而不用重開工廠或換掉原料。唯一的代價是 人類理解與協作的成本(需求確認、程式設計、測試、部署),而不是物理世界的限制。
🪵 甚麼? 又要改程式?理解了軟體的本質後,我們可以接受「改程式是必然」,但依舊會覺得頭痛。為什麼 ...
Config Builder
還記得第一次接觸大型專案時的場景嗎?到處散落著不同的設定檔,有人用 JSON,有人用 XML,還有人直接在程式碼裡寫死一堆參數。結果要切換環境時,就像在拼一個缺角的拼圖:少一個值,系統就崩潰。
這就好比一場樂團演奏,每個樂手都拿著自己的譜,卻沒有人統一節拍。開發人員成了指揮,卻只能不停喊:「誰的設定跑掉了!」。
而 .NET Core 的 ConfigurationBuilder 就像是一個樂譜整理師,它把不同來源的設定集中起來,統一成一份總譜。無論是 JSON、環境變數,還是命令列參數,都能被收編進來,讓系統在啟動時就能「合奏」出正確的旋律。
⚙️ .NET Core 組態(Configuration).NET Core 非常依賴 Builder 模式,而「組態(Configuration)」就是其中一個代表性範例。
Configuration 系統就像一個「設定管理中心」,它把不同來源的設定(JSON 檔、環境變數、命令列參數…)集中起來,讓你用一致的方式讀取與管理。
⚙️ ConfigurationBuilder 的角色在 .NET Core 裡,Configura ...
注入之森裡的嚮導 - Autofac
寫程式有時很像在蓋房子。剛開始是一間小木屋,只有幾個房間,想要新增功能時,就直接在牆上多開一扇門或窗,簡單粗暴。可隨著專案變大,房間越來越多、走道越來越複雜,你會發現:
有些門只接受某一把特殊的鑰匙(類別直接依賴具體實作,難以替換)。
有些門打不開,因為它需要另一個房間的鑰匙,而那個房間又反過來要依賴這個(循環依賴)。
有些地方明明只是臨時搭的走道,卻意外成了主要通道,導致一改就全系統崩潰。
這些問題,就是物件之間錯綜複雜的依賴關係。當專案還小的時候,我們可能不會覺得有什麼大不了;但當專案逐漸長成一座龐大的建築,沒有良好的規劃,就會迷路、卡關,甚至動一根樑就牽動全局。
這時候,一個好的 IoC 容器,就像是「建築師」。它幫你畫清楚藍圖,規範房間與房間之間該怎麼互通,並且在需要的時候,自動幫你準備好正確的鑰匙。
🪵 好的 IoC 容器我們期望一個好的 IoC 容器要能做到
在大型專案中不會亂拋錯
易於使用,學習成本不能太高,以利維護
功能強大,能支援多種注入方式(建構式、屬性、方法)
錯誤提示友善,找錯誤不能像踩地雷一樣
生命週期管理靈活,可以細緻地控制物件生命週期
相容性強,能 ...
Options Pattern
這就像搬家時,把東西胡亂裝在箱子裡,搬到新家後才發現少了零件、找不到工具。而 Options Pattern,就像是給每個箱子都貼上清楚的標籤,還附上說明書,無論是開發、測試還是上線環境,都能安心打開箱子,拿到正確的東西。
Options Pattern 是把 appsettings.json 裡的設定資料,轉成一個 C# 類別,然後透過「依賴注入(DI)」的方式注入到你的服務中使用。
⚙️ 編譯時類型檢查(Compile-time Type Checking)你把設定對應到一個 C# 類別,這代表你在編譯階段就能知道型別對不對,不是等到跑程式才發現爆炸。
12var smtpServer = Configuration["Email:SmtpServer"]; // 傳回 string,錯了也不會報錯var port = int.Parse(Configuration["Email:Port"]); // 如果 Port 是 null 或字串,這邊才會爆炸
Options Pattern 作法(強型別):
12345678public cl ...
Overload
對使用 API 的開發者來說。當他們呼叫一個方法時,心中想的只是「我要讓它運作」,卻不希望因為情境稍有不同,就被迫學習一整套全新的操作方式。這就是 Overload(方法重載) 存在的理由,它讓同一個「旋律」能被多種方式演奏,保持一致性,同時降低學習與使用的心智負擔。
🌺 框架裡的 OverloadConsole.WriteLine()我們平常在寫 Console.WriteLine() 的時候,可以丟 string、int、bool、甚至物件,對吧?因為每種型別印出來的方式不一定一樣,比如物件就會呼叫 ToString(),而 string 不需要再轉。
12345Console.WriteLine("Hello"); // stringConsole.WriteLine(123); // intConsole.WriteLine(3.14); // doubleConsole.WriteLine(true); // boolConsole.WriteLine(new DateTime(2025, 8 ...
HttpRequestMessage
在資訊的天空裡,每一次請求都是一隻信鴿。它揹著我們的訊息,穿過纜線與路由器,飛往未知的彼端。
但如果我們只急著把字條塞進它的翅膀,信鴿或許能起飛,卻未必能安全抵達。它需要一個完整的「航行指令」:要飛向哪裡、攜帶什麼、如何證明身份、以何種姿態展現誠意。
HttpRequestMessage 就是這份航行指令。它讓我們能在雲端的長風中,為信使裝備地圖、護照與行李,確保訊息能準確無誤地抵達遠方。
📨 HttpRequestMessageHttpRequestMessage 的存在就是為了讓 HTTP 請求能「物件化」並且「延後執行」,這是一種「Command Pattern」的應用:
組好請求指令 → 再交給執行者 HttpClient → 再送出
這樣做的好處是:
請求可以先組好但晚點再送
可以重複使用或修改
可以做攔截、紀錄、包裝(例如透過 DelegatingHandler)
HttpRequestMessage 是一個「請求的包裹」,你可以把它裝滿所有你要送 ...
Refit
在古老的時代,國王要把詔令送往四方,往往需要派出一位信使。但這位信使總得記住每一段路線、每一條小徑,甚至還要自己翻譯當地的語言。每一次派遣,不僅耗費心力,也總有出錯的風險。
程式世界裡的我們,也常常面臨相同的窘境。每次要呼叫第三方 API,就得重複 HttpClient、手動處理序列化與反序列化,還要加上各種 Retry、Logging。久而久之,這些細節就像一張張散落的地圖,讓信使負擔沈重。
而 Refit 的出現,就像給我們一套「信使專屬的傳令書」。只要定義好任務(Interface),信使便能自動遵循指令,把訊息送達遠方,並且用我們熟悉的語言回報結果。從此,我們不必再煩惱翻譯、路線和格式的細節,只需專注於該傳遞的訊息本身。
他讓我們能夠以 Interface 來定義 怎麼串接第三方 API 。
所以我們不需要直接使用 HTTPClient,而是定義一個 Interface,Refit 會將 Interface 的方法包裝起來,處理 HTTP 請求並將 Response 數據序列化為 Interface 中指定的類型,還可以組上自製的 HttpMessageHandler 以及處理 ...
HttpClientFactory
在浩瀚的網路之海,每一次 API 呼叫,就像派出一位無聲的信使。他背著我們的訊息,穿越纜線與雲層,去敲響遠方伺服器的大門。而我們該如何訓練這些信使?如何確保他們既不迷路,也不在途中耗盡力氣?這就是 HttpClientFactory 存在的理由。
📨 A. 方法內各自 CreateClient(匿名信使)每次出征時,我們都臨時徵召一位信使,並告訴他要去的目的地與身份證明。信使們雖然每次看似「新生」,但其實都共享一條秘密通道(Handler Pool),因此不會因為頻繁占用多個港口。但缺點是每次都要重複交代路線與口令,若忘了,就會讓信使迷途。
12345678910111213141516171819202122232425262728293031public void ConfigureServices(IServiceCollection services){ services.AddHttpClient(); services.AddRazorPages();}public class LineService : ILineService ...
Socket Exhaustion - 當臨時的港口被耗盡
在浩瀚的網路之海,每一次 API 呼叫,就像放出一位無聲的信使。他揹著我們的訊息,穿越纜線、翻越路由器,終於抵達遠方的伺服器。然而,這段旅程並非沒有代價。
當信使啟程時,作業系統會替他分配一個「臨時的港口」(ephemeral port),讓他能安全啟航。但若我們過於頻繁地呼喚、過於急切地派遣信使,這些港口就會一個接一個被佔滿。直到最後,新信使無處登船——這就是 Socket Exhaustion 的隱憂。
📨 TCP 4-tuple要出海,信使需要四件通關憑證:
假設我們今天上 Google 首頁,其實就是建立一條 TCP 連線,這條連線會用到這「四件東西」來唯一識別這條通道:
項目
說明
你的 IP(來源 IP)
例如:192.168.1.100
你的 Port(來源 Port)
⚠️ 是作業系統隨機分配的,例如 51234
伺服器的 IP(目的 IP)
例如:142.250.190.46
伺服器的 Port(目的 Port)
✅ 這通常是 80(HTTP)或 443(HTTPS)
這四個合起來叫做:TCP 4-tuple就像護照、簽證、船票與碼 ...
Socket
在這個時代,我們打下一行簡單的程式碼,資料便能穿越海底電纜、飛越路由器之間的節點,抵達遙遠的伺服器。這一切對我們來說只是「一次 API 呼叫」,卻在背後上演著一段浩瀚的旅程。
想像有一位無聲的信使,他從你的程式出發,手中捧著訊息,沿著電纜與協定鋪展的道路一路前行。途中他會經過城市的路由器、跨越國界的骨幹網路、穿梭雲端的防護牆,最終抵達遙遠的伺服器大門。完成使命後,他又沿著相同的路徑回返,把遠方的回應帶回到你眼前。
這位信使,就是 Socket。他是隱形的橋樑,是程式與世界的通道。在鍵盤與螢幕的另一端,他靜靜地,卻堅定地奔走於無形的網路之海。
📨 Socket 是什麼?Socket 可以理解為「程式」與「網路」之間的連接點(endpoint)。它讓你的程式能透過 IP 和 Port 與外界通訊,發送或接收資料。
舉例來說:
當你寫了一個 Web Server,你會讓它透過一個 Socket 在 80 port 上「監聽」(listen)。
瀏覽器想連到你的網站時,會建立一個 client socket,去「連線」你的 server socket。
而 Port 是區分 ...