「源碼風格」究竟能解決什麼問題?(下)
看得出會錯
與其說這是「風格」,還不如說這是個「範式(Paradigm)」。各家語言會有自己發明的一些機制,像是C#的「Coroutine」或是Java的「JNI」等,但扣掉那些語言自身特性的東西,一些變數,函式或類別的概念還是一致的,所以一份好的源碼,它應該總是要維持著一種容易「看得出有錯」的慣例,就像:
- 有變數就要有初始值
- 有參數就要有檢查
- 有回傳值就要馬上給
前兩個還比較好理解,「3.」的意思是這樣的:
int get_person_count()
{
int rtn = 0;
// …(先把回傳值寫好,再寫內容。)...
return rtn;
}
這意思是說,即便你寫到一半被叫去開會,或是你的源碼交給另一個人維護了,當程式運行起來在這裡有疑慮的時候,你完全能第一時間就反應過來:「我有實作嗎?如果沒有,它必然是傳回0」。
這樣的慣例建立起來之後,再參與源碼覆審時(code review)時,你只要看到這些狀況出來的時候,你就能很快的反應出來這是一個有危險的寫法,很容易〝看得出會錯〞。
寧可重構,也不要架構有錯
這其實不算是風格問題,但卻是個能讓你的源碼〝保鮮〞多久的問題。在錯誤的架構設計下,許多一開始規劃好的源碼到最後都會像整修前的中港大排那樣惡臭連連(因為什麼鬼東西都排到裡面去…),而發臭的源碼必然就會直接影響產品的品質。
有類別就要給角色,想介面
C語言是一種只有結構(Struct)而沒有類別(Class)的語言,因此〝角色〞的觀念並沒有那麼強烈及重要。但目前一線的主流語言,都有類別這樣的資料結構,在設計上就要常常覆審一下,它現在有脫稿演出嗎?在處理網路功能類別的成員函式實作中,它有處理視窗顯示或用戶交互的部分嗎?在遊戲引擎的底層是否有包入了專案的規格,以取得更簡便的處理嗎?我新增了一個新的類別,有新的實作,我能〝明確〞的說出,這個類別的實體是專注在解決什麼問題嗎?
當一個類別被發明出來,它就必須要在整個工作流中有明確的角色。有了明確的角色,才會有明確的介面。如同剛剛舉的例子,或許在某個連線功能的對話框由網路函式庫來顯示,在技術上沒有問題,用戶也不會覺得有什麼問題,但隨著規格開始群魔亂舞(遊戲產品的規格變化之大,這樣形容不會太過份…😅)的時候,你的產品就會開始敗象盡露,同功能的源碼必須在多個類別都複製一份,不同的功能會有同樣的bug,很多很靈異的事情就會開始冒出來。
開立介面這件事,是維持品質中蠻容易被忽略的一件事。在軟體的世界中,工程師就是神,神可以決定要不要讓用戶的許願成真。所以,就像「王牌天神(Bruce Almighty)」中演的那樣,你要是來者不拒,很快的就會出大事。用戶用了你的框架或函式庫,他就有絕對的權力決定是特定的函式是每frame呼叫一次還是每秒呼叫一次;是只呼叫A不呼叫B,還是先呼叫A再呼叫B…各種奇葩的用法組合,會隨著你開出的介面數量成等比級數上升。用了你的框架或函式庫,當然就是你要負責解決用戶的問題。
你只是想要身體,還是想要建立關係?
這標題好聳動啊…別誤會,我指的是「實作」和「從屬關係」。
物件導向設計中,常見的一個問題是「何時該繼承vs何時該組合」。這種問題的詳細回答及舉例都很多,我這邊給同事的一個簡單判斷法通常都蠻管用的:
你只是要它的功能,還是要跟它建立從屬關係?
只是要功能,那就拿它的實體(Instance)來用,要建立關係才考慮繼承為它的子民。
〝雜交〞問題通常都出現在C++,所以的語言都很有默契的拿掉這個能力,但不是代表這個能力有問題。我自己用過那些只能單一繼承的語言,往往就覺得〝組合〞那些功能起來是很沒生產力的事情。我有一本有聲書,打開到第5頁的時候會發出語音說故事給我聽,它為什麼只能從「書」和「多媒體」中選一種重生(spawn)呢?所以想清楚你的需求是什麼,能適當的使用,就能讓產品的品質不但一樣穩固,還更有彈性應付規格的變更。
你有FreeStyle嗎?
我自己的源碼風格可幾乎說是「沒有風格」。什麼區域變數要先用一個〝m_〞當前綴,還是用空白不用跳格的,我都沒有。我的大括號永遠不會在同一列,symbol的名稱清一色就是「aaa_bbb_ccc」全小寫用底線分隔而已。我沒有「FreeStyle」,我只有「FixedStyle」,搭配一些前人的經驗濃縮出來的「Convention」及「Paradigm」能讓我的程式比別人穩健一點,比別人的好懂一點,好維護一點,我想這樣對「解決問題」及「改善流程」應該會比較有幫助吧。