Re: [討論] 寫程式的追求?

看板Soft_Job (軟體人)作者 (PCMan)時間3周前 (2025/05/22 17:01), 3周前編輯推噓21(21055)
留言76則, 24人參與, 2周前最新討論串18/19 (看更多)
感謝大家熱烈討論,看到不少網友對 interface 的話題有興趣, 另外開一篇,歡迎大家一起來討論 : -- : ※ 發信站: 批踢踢實業坊(ptt.cc), 來自: 73.70.25.48 (美國) : ※ 文章網址: https://www.ptt.cc/bbs/Soft_Job/M.1747295625.A.D41.html : 推 ikachann: 很多都這樣 一開始能動最重要,真的有閒穩定下來後才是 05/15 22:20 : → ikachann: 重構的部分 05/15 22:20 專案開發節奏跟著商業需求走的,需要速度的時候先能動,但方向確立後,就真的需要 排時間做 refactor,清理成好維護的 code, 不然容易快速 launch,然後就快速墜落 refactor 的時候往往遇到一些 code 無法寫 UT,這時候就是該導入 abstraction layer 的時候了。 : 推 ikachann: interface真的是說到點了,可能補習班教吧 一堆人都照著 05/15 22:22 : → ikachann: 一個service都做一個interface然後就只有自己實作 05/15 22:22 : → neo5277: interface 還是看語言跟框架用啊 05/15 22:42 : 推 stepnight: interface 跟 DDD 就是互相成就 05/15 23:5 : 推 shibin: 推,好奇問,那如果是為了 UT 而弄的 interface 呢? 05/16 14:3 : 推 attacksoil: 有時候寫interface是怕把依賴方向搞爛 有建議嗎 05/17 22:2 : 推 tsaigi: 但c#要mock的話不是一定要interface嗎? 不測試的話的確 05/21 11:0 : → tsaigi: 是不用interface啦 聊到這個,除了 interface 還有 oop 繼承的問題,早年學 OOP 都是狂用繼承 但幫 parent class 改變行為,所有 children 就強迫一起自動獲得這個改變, 如果有部分 children 不需要這個行為,事情就會變得非常棘手。 這就是為什麼會有 SOLID principle 的 O, open-close principle parent class 要設計成好開放擴充 (open) 但 parent class 本身不能隨便改(close) 但這實務上不好做到,所以現在越來越常見提倡少用繼承,尤其不要為了 reuse 某個 class 的功能而繼承他。如果 class A 需要 reuse class B 的功能,可以在 class A 的 data member 建立一個 B 物件,然後呼叫他就好,不需要繼承整個 B 的全部行為跟 介面,就可以有效避免這問題。 這個就是 compoisition over inheritence, 透過組合多個物件來 reuse 他們的功能,而不是繼承他們的 classes 來取得這些功能。 當這樣做的時候,每個物件就可以只負責一件事情 (single responsibility), 他們就會很單純很好測試,然後組合這堆物件來合作完成一個大功能。 每個物件就可以有很簡單的介面,這就是 SOLID 的 S, single responsibility 這樣對 testability 也有幫助,但這樣衍生的問題,就是物件會拆得很多很散, 把這些東西正確兜再一起很複雜,於是就需要寫 factory 來建造這些物件, 或是借助 dependency injection 工具,使得 dependency 的管理變複雜。 物件之間要正確的互動也複雜,這個的經典案例就是 Windows 輸入法框架 tsf 每個 interface 都只有一個功能,大多只有一個 method,結果就是寫個輸入法 API 文件一打開散成上百個 interfaces,沒有足夠的範例誰知道要從何看起? 這種時候就需要,提供一個簡單易用的 wrapper,來把這堆複雜互動包起來, 提供簡單 API 讓常用場景方便使用,這就是 facade pattern。 解決一個問題,往往會製造另外一個問題,所以最後你逐漸就上了軟體工程全餐 : 推 stepnight: interface 跟 DDD 就是互相成就 05/15 23:56 : 推 shibin: 推,好奇問,那如果是為了 UT 而弄的 interface 呢? 05/16 14:39 如果是為了讓某些 dependency 在測試期間能替換,這是合理的做法。 最常見的場景之一,就是把 clock API 包進 abstract interface 裡面,然後 code 需要時間就 call abstract interface,而不是直接呼叫 system call 這樣在 UT 裡面就可以替換成假的,inject 寫好的固定時間進去,不然每次跑都不一樣 另外一個例子就是你的程式呼叫某個系統 API,但 UT 的環境不允許呼叫,或是每次結果 是隨機的,那我們就會用 interface 把它包起來,讓 UT 時可以替換成不同實作 這確實是很合理而且常見的作法。但除了這些特殊狀況,其他多半不是必要的 測試的時候還是 Prefer real object,使用真實物件如果適合,盡量用真的 使用 fake or stub 每次你改變行為,這些 fake/stub/mock 也要跟著連動,如果沒有 一起改到,UT 就可能會壞掉或是得出錯誤的結果,使用過度會非常難維護 test double 使用的目的,主要是為了觀察內部互動 (mock),提供假的固定數值 (stub) 或是避免外部依賴,例如避免連真實 server,或是加速,例如避免大量 disk I/O, 換成用 in-memory cache 的實作來加速 UT,除了這些狀況,大多都可用 real object : 推 attacksoil: 有時候寫interface是怕把依賴方向搞爛 有建議嗎 05/17 22:28 我想你在講的是 inversion of control or dependency inversion,也就是 SOLID 的 D 他要解決的問題是物件間高度耦合,所以讓大家都依賴高階抽象 interface,不直接 呼叫低階的實作細節,低階的實作要呼叫其他物件,也透過高階介面,所以稱"反轉" 大家都只依賴介面而非特定實作,就達成低度耦合。 (註: Inversion of control (dependency injection) 和 dependency inversion 是不 同的東西,但實際上幾乎會一起用,因為所有呼叫都是 abstract interface 的話, 那使用的 code 大多不會負責建造這些物件,所以幾乎必然會從外面 inject 進來) 這要解決的問題是耦合,但耦合不等於不好。當兩個物件就只有一種實作,然後他們就 真的只能搭配一起用無法抽換,那耦合其實也沒有問題,沒必要硬要解耦。 等你需要抽換不同實作的時候,再來抽 interface 其實常常也沒關係。 會有問題的是 share library,事後補抽 interface 會變動 ABI,造成 binary 版本 不相容,需要重新編譯。這種情境 interface 一開始訂好不要動就會比較好。 然後 interface 越小越好,每個都只做最少的事情,就不會動一個東西就要到處改 這樣對測試維護都比較好,也就是 SOLID 的 I, interface segregation : 推 tsaigi: 但c#要mock的話不是一定要interface嗎? 不測試的話的確 05/21 11:00 : → tsaigi: 是不用interface啦 05/21 11:00 上面討論過 UT 了 : 推 jennya: 熱愛嘗試當下流行的新工具新理論的工程師,常常也是團隊主 05/22 01:58 : → jennya: 力,很難找到理由阻止他們嘗試新事物 05/22 01:58 確實,就只能定期 review,定期 refactor 了。 對導入新事物保持開放心態,同時也對萬一發現不合適隨時 rollback 保持開放 勇於嘗試,但也勇於承認錯誤,就會是個比較健康的文化。 講半天架構,耦合,好玩的是,實務上最後決定系統架構的,往往是 Conway's law 你的組織架構長怎樣,系統架構最後就會長成那樣.... XDDDD 雖然現在 AI 當紅,還是很高興有人一起來討論這些日薄西山的傳統軟體工程啦! (淚) -- Sent from PCMan on PCMan's PC -- ※ 發信站: 批踢踢實業坊(ptt.cc), 來自: 73.70.25.48 (美國) ※ 文章網址: https://www.ptt.cc/bbs/Soft_Job/M.1747904496.A.E67.html

05/22 18:30, 3周前 , 1F
C#來說虛擬類別出來然後可以有不同實作又可以再掛介面
05/22 18:30, 1F

05/22 18:31, 3周前 , 2F
目前我是覺得我的場景還算滿夠用
05/22 18:31, 2F

05/22 18:35, 3周前 , 3F
有時候覺得OO搞那麼複雜 不如C語言
05/22 18:35, 3F

05/22 18:35, 3周前 , 4F
至少所見即所得
05/22 18:35, 4F

05/22 18:35, 3周前 , 5F
新人也能trace code
05/22 18:35, 5F

05/22 19:05, 3周前 , 6F
推 為了隔離第三方呼叫而弄的 interface 是可接受的
05/22 19:05, 6F

05/22 19:06, 3周前 , 7F
其餘等不同實作開始浮現再來抽
05/22 19:06, 7F

05/22 19:07, 3周前 , 8F
是說 C 也是會使用 function pointer 解耦
05/22 19:07, 8F

05/22 22:13, 3周前 , 9F
後來發現OO寫到後面太複雜了就往functional靠了
05/22 22:13, 9F

05/22 22:43, 3周前 , 10F
推這篇
05/22 22:43, 10F

05/22 22:48, 3周前 , 11F
推推 不過SOLID的DIP跟IoC的DI應該是不同的東西?
05/22 22:48, 11F

05/23 00:32, 3周前 , 12F
推分享~好專業
05/23 00:32, 12F

05/23 01:20, 3周前 , 13F
在這個AI產UT的時代 更不該隨意抽象
05/23 01:20, 13F

05/23 01:20, 3周前 , 14F
發現有模組能共用再抽象出來就好了
05/23 01:20, 14F

05/23 01:24, 3周前 , 15F
樓上說的沒錯,dependency inversion & injection 不同
05/23 01:24, 15F

05/23 01:25, 3周前 , 16F
但兩者實務上常並用,我寫清楚好了,感謝糾正
05/23 01:25, 16F
※ 編輯: HZYSoft (104.135.204.212 美國), 05/23/2025 01:28:17

05/23 01:28, 3周前 , 17F
搞清楚每個東西是解決什麼問題,就可以知道什麼時候要用
05/23 01:28, 17F

05/23 01:29, 3周前 , 18F
但用了一個往往就製造另一個問題,最後就整個套餐集滿了
05/23 01:29, 18F

05/23 01:31, 3周前 , 19F
最後就分開看都是 best practice,但全部組起來卻看不懂
05/23 01:31, 19F

05/23 01:31, 3周前 , 20F
在大型專案有些複雜度有必要之惡,了解脈落有助於理解
05/23 01:31, 20F

05/23 07:02, 3周前 , 21F
Accidental Complexity,不意外。
05/23 07:02, 21F

05/23 07:10, 3周前 , 22F
Uncle Bob的SOLID都是一看合理但實際行不通的觀念。當作
05/23 07:10, 22F

05/23 07:11, 3周前 , 23F
理想可以,但實務是折磨自己。
05/23 07:11, 23F

05/23 07:37, 3周前 , 24F
推推
05/23 07:37, 24F

05/23 09:45, 3周前 , 25F
組合起來就看不懂,有機會是boundary切太大,有多個職責在
05/23 09:45, 25F

05/23 09:48, 3周前 , 26F
裡面。工程師要直接面對程式碼,視角上難免見樹不見林
05/23 09:48, 26F

05/23 09:50, 3周前 , 27F
常常遇到SA/PM開需求都不加思索,畫面上有甚麼就開甚麼需求
05/23 09:50, 27F

05/23 09:50, 3周前 , 28F
任何複雜的商業邏輯,都被他們開得好像只要CRUD就能完成 XD
05/23 09:50, 28F

05/23 09:58, 3周前 , 29F
謝謝大大分享,我經驗不是很多,主要是寫獨立遊戲,現在
05/23 09:58, 29F

05/23 09:59, 3周前 , 30F
用的很接近你的軟體工程全餐,但是我使用起來感覺很條理
05/23 09:59, 30F

05/23 09:59, 3周前 , 31F
system+factory+facade組一個系統,寫完了丟namespace
05/23 09:59, 31F

05/23 10:00, 3周前 , 32F
對項目整體複雜度下降真的很有幫助。沒用設計模式之前
05/23 10:00, 32F

05/23 10:00, 3周前 , 33F
寫出來的東西對心智負擔很大
05/23 10:00, 33F

05/23 10:01, 3周前 , 34F
用設計模式不算壞事吧?
05/23 10:01, 34F

05/23 11:36, 3周前 , 35F
現在我幾乎不寫單元測試而是直接寫整合測試把自己當使
05/23 11:36, 35F

05/23 11:36, 3周前 , 36F
用者去測完整行為了,本地端直接docker compose啟Post
05/23 11:36, 36F

05/23 11:36, 3周前 , 37F
gres Redis等實在很方便,早期docker還沒那麼成熟時只
05/23 11:36, 37F

05/23 11:36, 3周前 , 38F
能靠h2假裝一下…
05/23 11:36, 38F

05/23 13:13, 3周前 , 39F
說這麼多,其實我只是想提倡徹底理解各種practice的優缺
05/23 13:13, 39F

05/23 13:14, 3周前 , 40F
比起把書上的東西當成教條奉行,更重要的是理解其內涵
05/23 13:14, 40F

05/23 13:14, 3周前 , 41F
了解他們解決的問題和侷限,使用上就會更彈性和得心應手
05/23 13:14, 41F

05/23 13:14, 3周前 , 42F
就能吃軟工自助餐,想要什麼就夾什麼,知道自己在做什麼
05/23 13:14, 42F

05/23 20:51, 3周前 , 43F
不就是為了一定要用某個概念,有沒有多偉大不知道。
05/23 20:51, 43F

05/23 21:09, 3周前 , 44F
但這個概念產後的副作用另一個概念/方法來補救。
05/23 21:09, 44F

05/23 23:28, 3周前 , 45F
到最後c++我還是用free function > class xd
05/23 23:28, 45F

05/24 00:49, 3周前 , 46F
推徹底理解各種practice的優缺
05/24 00:49, 46F

05/24 08:08, 3周前 , 47F
有時候人在江湖 身不由己。 每次上面老闆急著要東西
05/24 08:08, 47F

05/24 08:08, 3周前 , 48F
要你最快速趕出來。什麼clean code,design的東西全
05/24 08:08, 48F

05/24 08:08, 3周前 , 49F
丟在旁邊,老闆看到能跑就爽了
05/24 08:08, 49F

05/24 08:10, 3周前 , 50F
之後想要跟上面要時間去整理一下code, 好以後擴張,
05/24 08:10, 50F

05/24 08:10, 3周前 , 51F
上面就會一副不耐煩的表情說不用動
05/24 08:10, 51F

05/24 08:10, 3周前 , 52F
之後需要擴張時上面的又會嫌說當初怎麼寫的那麼亂很
05/24 08:10, 52F

05/24 08:10, 3周前 , 53F
難擴張
05/24 08:10, 53F

05/24 11:47, 3周前 , 54F
了解了 推推
05/24 11:47, 54F

05/24 18:47, 3周前 , 55F
然後技術債就是大家比偷渡,用三寸不爛之舌把一些有隱
05/24 18:47, 55F

05/24 18:47, 3周前 , 56F
憂的部分想盡辦法偷渡進下一個專案的排程
05/24 18:47, 56F

05/24 19:02, 3周前 , 57F
我個人看不順眼的code重構也是包在自己的時間內
05/24 19:02, 57F

05/24 19:03, 3周前 , 58F
重構也不是整個重寫,評估處理個大概能用
05/24 19:03, 58F

05/24 19:04, 3周前 , 59F
就好
05/24 19:04, 59F

05/25 11:09, 2周前 , 60F
project重構本來就應該包,只是很多人不接受
05/25 11:09, 60F

05/26 16:11, 2周前 , 61F
就是過於複雜了 語言特性決定一切 只有麻煩的語言才
05/26 16:11, 61F

05/26 16:13, 2周前 , 62F
講究這些東西 然後還有些人當成寶 包含框架都是 我很
05/26 16:13, 62F

05/26 16:14, 2周前 , 63F
久以前就看破這情況 簡單並不等於實現複雜功能麻煩
05/26 16:14, 63F

05/26 16:18, 2周前 , 64F
然後有些軟體整策略整半天 最後有差嗎? 真的快笑死
05/26 16:18, 64F

05/26 16:20, 2周前 , 65F
軟工要學的是本質而不是整天搞漸近式優化
05/26 16:20, 65F

05/26 16:24, 2周前 , 66F
然後過於注重小細節 一個小寫法就搞定的東西
05/26 16:24, 66F

05/26 16:31, 2周前 , 67F
這個工作一兩年應該就知道了 很可惜這一堆人樂此不疲
05/26 16:31, 67F

05/26 16:35, 2周前 , 68F
資安也是 引入那麼多東西不怕這些東西有問題... 只有
05/26 16:35, 68F

05/26 16:36, 2周前 , 69F
能掌控的才能夠全方位覆蓋全部
05/26 16:36, 69F

05/26 16:41, 2周前 , 70F
說真的還不用ai 只要多點這種人 可惜現實不可能 為了
05/26 16:41, 70F

05/26 16:41, 2周前 , 71F
利益是人的本性
05/26 16:41, 71F

05/27 18:43, 2周前 , 72F
超熊貓大大...好厲害...
05/27 18:43, 72F

05/27 22:56, 2周前 , 73F
還可以
05/27 22:56, 73F

05/28 05:19, 2周前 , 74F
推一個
05/28 05:19, 74F

05/28 11:18, 2周前 , 75F
像c++就用他的lib就好 沒事別在那邊定義class
05/28 11:18, 75F

05/30 00:13, 2周前 , 76F
那就是C的寫法
05/30 00:13, 76F
文章代碼(AID): #1eBkVmvd (Soft_Job)
討論串 (同標題文章)
文章代碼(AID): #1eBkVmvd (Soft_Job)