程式碼的版面配置與風格樣式

什麼是程式碼的版面配置 (Layout)風格樣式 (Style)?用一句話簡單說明就是:程式碼呈現出來的樣貌

在程式碼的文字檔案中,除了有意義(或混亂)的程式碼與註解文字之外,佔據其他空間的正是所謂的版面配置元素;也就是程式碼中的空格、空行、斷行、縮排、括弧等元素。 正所謂「人要衣裝,程式要風格」,利用這些版面配置元素將程式碼好好打扮裝飾一番,不僅能夠讓程式碼看起來賞心悅目,使閱讀程式碼的人功力大增,更能夠讓程式碼的邏輯與結構更加清楚顯著地展現出來。可說是一舉數得、好處多多,真是「居家寫 Code,出門上班」的最佳良伴啊~

版面配置是對於程式結構的有用指引線索。良好的配置方案能夠使程式的視覺化結構與邏輯結構相符合,傳達給人類與電腦相同意義的訊息。在此要再次引用《Refactoring》書中所寫的一句經典:

Any fool can write code that a computer can understand. Good programmers write code that humans can understand.

在撰寫程式的過程裡,較少部分的工作成效,是為了使得電腦能夠瞭解程式碼;而較多部分的努力,是為了使得其他的人類能夠讀懂程式碼。在這個前提之下,程式碼如果能夠達到良好的視覺化配置,就可以恰如其份地展現出一個程式的邏輯結構。程式碼的配置和風格,不會影響到外在可見的因素,例如執行速度、記憶體使用等等,而是對於易讀性、可修改性有很大的影響。並且必須在程式專案初始建構的時期,就將這個議題納入考量。

《Code Complete, Second Edition》中,特別將 Layout and Style 這個主題獨立成一個章節來詳細闡述,可見得程式碼的版面配置,在軟體建構的程序中也有相當重要而不可忽視的地位。

程式碼版面配置的目的,在於達到程式碼邏輯結構的準確性一致性,並且增進可讀性可修改的能力。在討論應該使用的格式時,記得將這些需要達到的目標列入首要的考量因素。看起來好看只是其次的條件;達到其他目標,配置就會看起來好看。

保持一致性 (Consistency) 可說是撰寫程式這項任務中的首要之事;除了維持設計邏輯思維上的一致性,能夠使得閱讀者更容易瞭解程式整體架構的脈絡體系之外,達到程式碼外觀的一致性,也能夠大幅幫助閱讀程式碼的人更容易理解程式區塊中的邏輯結構。

對於一個團隊合作開發的程式專案來說,需要在專案一開始的時候,就以清楚的文件以及簡單的範例,訂立出詳細的程式碼配置準則與規定。如此所有團隊成員都能夠按照著規則撰寫程式碼,而不致產生選擇程式碼配置樣式上的困惑。同時也有利於幫助新進的團隊成員,更快速地瞭解龐大的程式庫細節。另外要注意的是,盡可能不要使這些準則規定太過於複雜,否則可能會降低程式撰寫者遵行規則的意願,而使原先訂立規則的良好用意變成徒勞無功了。

如果沒有訂立出明確的準則,容易產生模糊不清的狀況;程式碼撰寫者各自使用自己所習慣或喜愛的格式,對於其他閱讀者來說,可能會造成不小的困擾與疑惑。對於像遊戲製作這樣曠日費時的專案來說,從一開始就訂立出規則,並且在開發過程中時時保持一致性,更是相當重要而不可忽視的程序。如果在專案開始建構之前,忽略了這種看似小事的準則規範,將會在開發過程中帶來許多不必要的麻煩與痛苦。

將邏輯的呈現列為優先考量,通常不會產生醜陋的程式碼,除非是程式碼本身的邏輯就很醜陋。能夠使好的程式碼看起來好看壞的程式碼看起來難看,比讓所有程式碼看起來都同樣好看來得更加有用。

在除錯中容易忽略的問題,通常是因為我們自覺「認得」某部分的程式碼,而非真正的去閱讀程式碼而發現錯誤。而良好的版面配置,可以幫助我們更迅速地察覺出有問題的程式碼。版面配置,實際上就是提供了一種視覺上的提示 (Visual Hint) 作用,使得閱讀者能夠從視覺所取得的資訊中,立即做出直覺的反應與迅速的判斷。例如在程式編輯器中,常會使用不同的顏色標示關鍵字、註解、變數與字串等等不同作用的內容,也是為了提供程式撰寫者視覺上的提示與便利性。

版面配置是一種邏輯,同時也是一種美學上的議題,使得程式格式間的爭論,比較像是宗教信仰戰爭而非哲理上的探討。

事實上,相較於固執堅持特定結構化方法的種種細節,更重要的是維持一套一致性遵循的結構。不論是什麼樣的準則規範,只要能夠確實遵行,就能夠遠遠勝過沒有使用任何準則的程式專案。大部分的情況下,各持己見、爭執不同的程式撰寫標準,都是沒有必要的情形。

優秀的程式設計師,應該要對於自己的程式碼配置實例保持開放的心態,並且能夠接受經過實證後確實比較良好的配置方式。

除了維持程式碼的準確性與一致性之外,做為一個程式設計者,也要能夠保持著開放的心胸學習更佳的配置方式,並且接受程式碼外觀的演化能力。以我自己的例子來說,起初剛開始學習程式寫作時,我會盡量少用空白格與空行,想要使程式碼看起來簡潔有力:

for(int i=0; i<10; i++) {
    if (ThisIsTrue) MyFunction();
}

在逐漸累積程式撰寫的經驗後,才發現這樣的配置方式不只是眼睛看起來會比較疲累,也容易造成程式碼修改時的無心之過,或是程式除錯時的困擾。於是轉換風格為:

for ( int i = 0; i < 10; i ++ )
{
    if ( ThisIsTrue == true )
    {
        MyFunction();
    }
}

現在由於各種程式碼工具的發展與協助,使得版面配置與視覺提示的功能越來越強大且便利,所以我又轉換撰寫風格成:

for (int i=0; i<10; i++)
{
    if (ThisIsTrue)
    {
        MyFunction();
    }
}

以上並不是所謂的最好的配置風格,而是目前在我的使用經驗與習慣上認為比較合適的配置風格,將來同樣會不斷地改進並且演化。如果想要比較各種不同配置方式的優劣勝敗之處,在《Code Complete, Second Edition》書中關於程式區塊 (Code Block) 的各種風格形式與意義內涵,有深入且詳細的探討,是相當值得細讀的章節。

除了自己撰寫的程式碼之外,不同的程式語言、程式庫與遊戲引擎,也會有不盡相同的撰寫風格與版面配置。一般來說,遵守程式語言的慣例,能夠使熟悉相同程式語言的閱讀者快速地瞭解程式。而對於其他的第三方程式庫或遊戲引擎,是否要遵行它們所使用的準則規定?如果同時使用多種不同風格的程式庫要怎麼取決?對於程式庫、框架與引擎的規則標準,通常需要視專案的實際狀況而定,並沒有所謂「一定如此」或「絕對不能這樣做」的規範。以下列出幾項需要考慮的基本項目:

  • 是否要使用匈牙利命名法標示變數的資料型態?
  • 是否要使用 Scoping Prefix 例如 g_ 與 m_ 來區別變數的 Scope?
  • 控制結構的前括號要放在同一行,還是自成一行?
  • Inline 的程式碼定義要直接寫在 .h 中,還是分開寫在 .inl 檔案中?
  • 如果單行程式碼的長度過長,要怎麼樣斷行?

LayoutStyle 的配置使用,是在程式撰寫活動中,與「人」最為相關的議題。我們寫程式的同時,也要閱讀很多的程式,不論是自己的、團隊成員的或外部第三方的程式碼。閱讀可以說是一種單向的溝通方式,閱讀者無法從撰寫者處得到立即的回應,只能夠使用自己的理解力盡可能瞭解撰寫者想要傳達的各種想法與意涵,所以如果寫出了高深莫測、難以維護的程式碼,並不能稱得上是值得驕傲的事。在程式撰寫的領域中,可不適合用什麼隱喻的手法來表現出自我的思維能力。如何能將程式碼中的意涵正確而完善地傳達給閱讀者,甚至是未來的自己,才是在撰寫程式時最最需要謹記於心並且著力實現的關鍵要點。

最後要提的是,由於部落格版面格式限制的緣故,所有的文章裡如果有寫到程式碼的部分,並不會嚴格遵行我個人的版面配置準則,而是以節省空間與不破壞文字版面為最大原則,以避免過長的程式碼會顯示在不正常的位置等等奇怪的情況出現。我在平常的程式撰寫中,習慣使用 4 個空格做為自動縮排的數量,但是在部落格的文章裡,為了節省每行的空間,我只會使用 2 個空格做為縮排格式,也可能會把原先單行的程式碼切斷成兩行以上;而平常寫程式我會使控制結構的前括號獨立成行,但是在部落格裡就會視情況而定,未必每次都會使用相同的規則囉。

好啦,不管你目前所參與的專案,或過往所寫的程式碼外表看起來有多醜,從現在開始,就幫它們好好的打扮打扮吧。未來的某天,會有某人非常感激你所做出的努力;而那個人,很可能就是你自己呢。

2008/02/24 Updated:更換了展示程式碼用的 Plugin。從現在開始,文章內的程式碼會回復成以 4 個空格做為自動縮排的數量,程式碼的長度也比較不會影響到部落格的版面囉!:D)

6 Replies to “程式碼的版面配置與風格樣式”

  1. 太讚同版主說的,從一開始寫程式到寫久了以後的風格轉變。剛學的時候總會跟同學比,看誰用最短的行數完成作業,拼了命要把東西放到同一行裡。後來發現這樣的程式真的太難看了,太過緊密的內容讓除錯時容易失焦,節省大括號的結果卻常常造成羅輯錯誤。大學教授應該要在一開始就教這個的!

    在一個小組裡,常常有一兩個很喜歡自我程式風格的人,而他們的風格總是離不開上面說到的那種、又密又小又不加註解,用了一堆define和printf來debug最後卻沒有移除的…真的會造成其他組員的很多不便。因此可以理解,面試之前為什麼主考人都會請你寄一些舊程式碼了。

    半路:
    實際情況的確是如此。
    所以 Code Review 的程序就很重要,不只能夠找出潛在的錯誤與臭蟲,更能夠讓所有人的版面配置與寫作風格逐漸趨於一致化。風格不一致的程式碼,真的會對整個團隊造成不少隱性但是要命的影響。

    要寄程式碼去面試工作的人,別忘了先整理一下自己程式碼的服裝儀容嘿~ XD

  2. Google C++ Style Guide

    這是許多 Google Open Source 專案所遵循的 Coding Style 文件。內容除了一般的程式碼撰寫風格規範之外,還有對於一些 C++ 特有的 Feature 做出使用與否的決定,例如 Default Arguments、Exceptions、RTTI 與 Casting 等等特徵,每一個項目都有寫出 Pros、Cons 意見,以及最終的 Decision。

    我覺得這份文件很有參考價值,可以按照自己的需求修改後,做為遊戲專案的 Coding Style 準則。

  3. @fr3@K:
    事實上,我就是看了您的文章,才知道有這份文件存在的。沒註明出處,真是不好意思。 Orz

    我也同意你的看法,覺得 Exception 不應該完全禁用。但是以文件中的說明來看,Google 由於 Legacy Code 整合不易的因素,而決定不採用 Exception 機制,我覺得也算是一項合理的考量。另外一點,或許是因為應用於開源專案之故,為了避免經常發生的 Exception 濫用與誤用情形,所以才採取比較保守的錯誤回報機制。

    我認為,這份文件的價值在於 Pros、Cons 與 Decision 的理由都寫得非常清楚明瞭,提供給程式設計者一個不錯的思考方向。至於是否完全同意其中所有的 Coding Decision?我想還是需要 Case-by-Case 攤開來檢視才行。畢竟,現實狀況總非如此理想可期,不是嗎? :P

    謝謝您的回應。

  4. 3 版容易改出 bug, 用 2 版比較好, 如以下

    for (int i = 0; i < 10; ++i)
    {
    if (true == ThisIsTrue)
    {
    Myfn1();
    }
    }

  5. @ca:
    你好,

    我的習慣是如果函式回傳值為 true 或 false 的話,便不會在條件判斷式中寫出來。你寫的這種 coding style 也同樣可行,我想只要維持一致的寫作風格就行囉。

Leave a Reply