記憶中曾經有個很特別的程式比賽,它要參賽者盡可能地寫出最難讀懂的程式碼。有一段C語言所寫成的程式碼在這個比賽中奪魁,這段程式碼的作用很簡單,只是在畫面上印出 「Hello World!」,但單看程式碼極難理解。

要寫下難以讀懂的程式碼固然困難,要寫出容易讀懂的程式碼卻也不容易。除了少數以明碼型式呈現的應用程式(例如以JavaScript寫成Web端應用程式),會刻意以人工的方式或自動化工具,產出人類難以讀懂的程式碼之外,對大多數的系統來說,程式碼的可讀性應當是越高越好。

撰寫程式時就要關注可讀性,別寄望日後有機會修整
話雖如此,卻不是每位程式人在撰寫程式時,都會留意程式碼的可讀性。每個開發團隊的管理者,也未必都會規範程式碼的可讀性。
對於許多人來說,在時程內完成能夠正確運作的程式碼,已經耗盡全部心力了,那有時間追求這多少稱得上是「奢侈」的要求呢?

乍看似乎如此,雖然所有人都明白可讀性高的程式碼容易維護,而且利於團隊共同的開發,但完成系統該有的功能,應當是最優先考量的,就算是「骯髒(dirty)」的方案,只要有助於盡快完成,那又何妨?至於程式碼的「可讀性」,不如就等開發的重要里程碑完成後,利用「農閒時期」再來整修(或者美其名為「重構」)一番(這當然誤解,也誤用了「重構」的意義)。

會抱持這種想法的團隊或程式人,其實很難找到真正的農閒時期,一個又一個的開發里程碑,總是會接踵而至,被視為優先順序較低的程式碼可讀性,就會一次又一次地被犧牲。

事實上,正確的態度應該是在撰寫程式碼的第一時間,就讓程式碼保持它應有的可讀性,而不是先寫下一個能動、但不見得好讀的程式碼,寄望日後還有機會將它整修成利於閱讀的型式。追求時限內完成工作,是個短期內必須達成的緊迫目標,但持續維持程式碼良好的可讀性,才是提升開發效率及品質的根本。如果總是在追求短期目標,多半只能治標而不能治本,無法從體質面改善。

求快而不重視可讀性的程式,反而會拖慢除錯的效率
為什麼良好的可讀性是系統健全體質的重要指標呢?它能如何幫助我們提升開發效率及品質呢?

很多人未曾深思,你或許只寫下程式碼「一次」,但這段程式碼卻會被閱讀許多次。可讀性高的程式碼,價值不單只是為了讀者,它對於撰寫者本身,同樣十分重要。相較於撰寫程式碼,程式人花在除錯或修改動作的時間更多。這意謂著即使處於開發階段,程式人花在閱讀程式碼的時間,仍然遠多過於撰寫的時間。

你貪圖一時之快,寫下不易閱讀的程式碼,再加上程式碼總是很難在第一時間就完全正確,所以必須接著除錯。因此,難以閱讀的程式碼成為程式人自行搭建的迷宮,在除錯的時候,也同時迷惑了自己,於是得花費更多的時間。

也許你有高超的記憶力,可以幫助自己在開發階段從迷宮中脫逃,但一段時日後,需要修改程式時,迷宮仍舊會再度登場挑戰你的記憶力。所以撰寫易讀的程式碼,不單有利他人,同時更是有助自己。良好的可讀性,可以省去許多的開發時間,當然可以提升開發的效率。面對具有良好可讀性的程式碼,程式人較不易犯錯或誤解,自然也會具有較佳的品質。

維持程式風格一致性,是可讀性的重要基礎
那麼如何撰寫具可讀性的程式碼呢?途徑其實有很多,我試著提供幾個重要的建議。

首先是一致性,它是可讀性的重要基礎。意指程式碼中所有的元素風格,都必須保持相同的規則,舉凡程式碼中的命名、縮排方式及註解的風格都得維持一致性。

一致性下的規律,讓讀者得以舉一反三;而缺乏一致性,則容易讓閱讀者產生混淆、難以適從。例如,有些物件導向程式語言的設計者,會將宣告為Public的函式,一律置放於檔案中的前處,在這樣的規律下,閱讀者心裡明白,Public函式是類別的介面所在,也是最重要的組成,而當閱讀者想瀏覽類別的Public函式時,會知道如何快速地找到正確的位置。

提煉函式濃縮程式碼,並提升可讀性
除了一致性外,建立程式碼的高階語義,也是提升可讀性的重要手段。通常,高階語言之所以較低階語言更具可讀性,原因在於高階語言的單一行程式碼,具有更高階的語義,換算成為低階語言,或許需要更多行程式碼,才能達成相同的目的。

所謂「建立程式碼的高階語義」,最簡單的例子,便是函式的提煉。有些程式人常犯「冗長函式(Long Method)」的問題,他們平鋪直述地在單一函式裡寫下數百行,甚至上千行的程式碼。倘若將這樣冗長函式中的程式碼,提煉成若干個子函式,並且賦予它們有意義的名稱,那麼這樣的函式便有可能因為運用子函式表達,而濃縮成為數十行。

對這個函式的閱讀者來說,一來語境變小,閱讀更輕鬆,二來更重要的是,低階的細節被子函式隱藏,所以閱讀的程式碼皆是更具高階意義的程式碼。讀者在理解時,是立足在高階的層次上,自然更容易明白程式碼的意義。

再來,應該「讓重要的部分更顯眼,讓不重要的部分隱晦」。如果你正在撰寫的程式碼,是希望讀者特別留意的部分,那麼應該試著加以強調。例如上述將類別的Public函式置於類別檔案的最前頭,便是一種方式。又例如不希望讀者太過關心的類別,即以內隱類別(Inner Class)的方式實作,一來達到封裝及資訊隱藏的目的,二來也是明白呈現出重要性的次序。

簡單、直覺的設計,將使程式更容易理解
另外,要「讓同樣羽毛顏色的鳥飛在一起」。也就是說,要將相同性質的程式碼放置在一塊。你會將高度相關的程式碼放在同一個類別裡,同樣的,你也應該將同性質的類別組織在一起。例如像Java提供的Package,或C++及C#提供的Namespace,便是要讓程式人將性質高度相關的類別,放置在同一個命名空間底下。當閱讀者要尋找與某一段程式碼相關的程式碼時,他會知道應該先就近找起。這樣的做法,可以提高尋找的效率及準確度。

盡量使用共通的語彙或慣例也是一種方法。雖然你可以自行發展具一致性的語彙或慣例,但最好的方式,便是採用廣泛被使用的語彙或慣例。例如像常見的設計模式,便是廣泛使用的語彙。使用設計模式,即使未以文件說明,讀者也能輕易地明白你想要表達的語義。

此外,請盡量採用簡單、直覺的設計。過於間接、迂迴的設計,通常都是複雜的,而複雜的設計通常都不會太好。換個角度重新思考,你多半可以得到等價,卻更為簡單、直覺的設計。這將使得程式更容易理解。

可讀性優於技巧展現及高效寫法
最後,可讀性優於技巧的展現,也優於效能。有些程式人會為了展現個人對程式語言的嫻熟度,在程式碼中刻意使用冷門、罕為人知的語言特性,這會大大降低程式碼的可讀性,也容易讓其他人犯錯。

而許多高效能的程式撰寫方式,會以可讀性做為代價。除非你確認某一段程式碼是效能瓶頸,否則千萬別太早最佳化。在第一時間,應該選擇可讀性佳的寫法,而非高效能的寫法。

作者簡介:
王建興
清華大學資訊工程系的博士研究生,研究興趣包括電腦網路、點對點網路、分散式網路管理、以及行動式代理人,專長則是Internet應用系統的開發。曾參與過的開發專案性質十分廣泛而且不同,從ERP、PC Game到P2P網路電話都在他的涉獵範圍之內。

熱門新聞

Advertisement