[208] 該死的範例碼之爭,卷一

[208] 該死的範例碼之爭,卷一

往下讀之前,請先回答這個提問:「文件上的範例碼該不該設計成適合『複製貼上』至真實專案?」

前篇 是我對網路爭論的處理態度。這一篇認真來看「該死的範例碼之爭」。起因是底下這篇公開文章:

覺得我對MS有偏見的人,請按下去看看MSDN官方的sample source code吧....誰在公司真的這麼寫,我一定會砍死他... (出處

批評的對象是微軟 MSDN 上的範例碼,被批的原因是使用了 Nested-if,而且多達九層甚至十一層。

扣掉那句「砍死他」,我同意原 Po 對使用該範例碼者的批評,若是在真實專案這麼寫,我也不能接受。不過,留言處歪得太厲害,有些觀念糟糕到讓人看不下去。手癢、頭皮癢,開始對牛彈琴。首先提問:

  1. 那是不是範例碼?
  2. 原 Po 知不知道那是範例碼?知道
  3. 該範例碼是否有改進空間?
  4. 該範例碼是否達成任務?八成
  5. 該範例碼是否有誘人直接用於真實專案的意圖?我認為沒有
  6. 能不能把該範例碼複製貼上到公司的專案裡直接套用?當然不可以
  7. 寫程式需不需要用腦?

該文件中的範例碼,其任務是解釋 Common Item Dialog 的各個 COM Interface 的用法,並示範了所謂的 Happy Path——刻意忽略許多錯誤處理,只處理成功路徑。我認為以此例來看,其範例碼的撰寫守則大概是:僅使用基本組件,避免使用額外的函式庫或輔助工具,而且忽略例外狀況的處理。

為什麼刻意不引入太多相依組件呢?舉個例子,若用 Qt 示範如何使用 std::cout 輸出 "Hello World" 至螢幕,可以說完全沒必要,單純以 C++ STL 來示範比較好。

實務上 ATL(Active Template Library) 有許多好用的工具可用來簡化 COM 程式設計,如 CComPtr<T> 可用來管理 COM 物件的生命週期(類似 C++11 std::shared_ptr)。ATL 雖然早內建於 Visual Studio,卻不是 COM 程式設計所必須。而該範例碼僅用到 COM 的基本組件,未使用 ATL 或其他輔具,不論是否刻意為之,我認為是正確的選擇。(我沒說 Nested-if 是好的寫法)

有幾個手段可以讓該範例碼的 Nested-if 消失。例如使用 Return Early 的手法搭配 CComPtr<T>。再搭配 Extract Method 的手法拆解函數。

這不是重構!這不是重構!

該範例碼若要導入至專案,肯定要改。別搞錯了,這可不是「重構」。重構的定義:

Code refactoring is the process of restructuring existing computer code—changing the factoringwithout changing its external behavior.

中文有人這麼解釋

在不改變軟體外部行為的前提下,改變其內部結構,使其更容易理解且易於修改

重點是「不改變軟體外部行為」,據此定義,進行「重構」後的範例碼還是範例碼。所以根本沒有「重構範例碼以用於專案」這種事。

你要做的是參考範例碼以理解其說明的對象之用法,然後視專案的需求來使用該些功能。請注意,實務上不應該允許「複製貼上」範例碼至專案的行為。若發現此行為,初犯警告,再犯則逐出師門,永不錄用。

使用範例碼時的注意事項:

  1. 範例碼通常只寫重點,絕對不能當做功能完整的程式直接套用
  2. 範例碼肯定不是針對你的專案而寫,參考時務必考量自身專案的實況
  3. 「複製貼上」範例碼到專案的行為是錯的,不要學

實務上若會用到 COM Interface,會選擇使用 ATL,藉此提昇程式碼可讀性與穩定性。開發穩定的程式,把程式碼寫得容易維護與修改的責任在我身上,沒寫好還怪起範例碼怎麼看都是推卸責任啊,師父是這麼教你的嗎?

用心良苦還被嫌棄?

換個角度想,或許該範例內建了「防範複製貼上程式開發大法」的機制啊?原因是那寫法若不做適當修改根本不能拿來用在真實專案。也就是說,該範例碼有可能是「故意」寫的不太好,就這一點來看,它其實做得很好。(謎之音:什麼跟什麼啦)

不相信?仔細看程式碼。挑最多層 if 的函數示例來看:

HRESULT BasicFileOpen()

複製貼上能用在真實專案,開玩笑的吧?

仔細看會發現,幾乎每一個 if 判斷式都有相應註解,用來說明該函數或變數的目的以及注意事項。這個範例碼的功用在說明介面的正確用法,而不是整體專案的架構。它不是要告訴你 BasicFileOpen 應該怎麼寫才漂亮,而是在說明 IFileDialog 這個介面的用法:如何生成物件,會用到哪些成員,使用的先後順序,還有要記得清除物件。

人家用心良苦,不識貨還怪東怪西,會不會太傲驕了點?難不成是鼓勵工程師使用「複製貼上程式開發大法」來做專案?

「可以預期大量新手會閉上眼睛copy-and-paste到自己的專案直接拿來用的。」這樣的新手就該抓來打屁股,給點教訓,教導他正確的作法。怎麼怪起範例碼寫手了呢?範例碼寫得再好,也不應該直接複製貼上到自己的專案。寫程式都不用動腦的嗎?

我從該範例得到以下資訊:

  1. IFileDialog 如何生成,以及各成員的用法與時機
  2. 如何判斷函數回傳值
  3. 如何取得 IFileDialogEvents 物件
  4. 應該傳入哪些參數至該些函數
  5. 如果正確清理物件

取得上述資訊後,要在專案裡使用 IFileDialog, IFileDialogEvents 時,我一定要照著範例碼的風格撰寫嗎?當然不。誰有責任寫好公司專案的程式碼?是微軟那位寫範例碼的傢伙?當然不。把自己的無能推到別人身上,能力太差怪罪範例碼也救不了你啊。

讀到這裡還認為我在鼓吹「範例碼可以亂寫一通」?請你重頭再看一遍。

卷一完,待續。


《大山姆的機機車車》電子報

我是山姆。2018 年初我發行了個人電子報,提供軟體開發資訊與學習資源,還有軟體工具介紹及使用技巧,其中包含了我在 WorkFlowy Tips 上發佈的最新文章。如果你跟我一樣求知若渴,又不想花太多時間「過濾」資料,每月兩期,著重在 C++ 與軟體開發議題的《大山姆的機機車車》電子報。資訊充滿,垃圾沒有,歡迎訂閱。