Saturday, March 7, 2009

從閱讀者心出發的註解寫作

Jan 15, 2009
從閱讀者心出發的註解寫作from 獨孤木 by qing

如果說有許多程式員不喜歡撰寫文件,那麼應該也有為數不少的程式碼不喜歡為他們所寫下的程式碼撰寫註解。撰寫註解和文件的工作,時常被許多程式員視為是浪費時間的工作。對他們而言,將心力專注在程式碼的產出,才是最實在的,文件和註解不過只是自己工作外的額外負擔,尤其是註解,並不像文件幾乎都會被列在專案的工作分解結構(WBS)中,更加不會被視為是程式員份內必要的工作,這就使得註解的地位似乎成了軟體開發工作中的雞肋一樣,倘若有,大家好像會覺得心安一點,因為程式碼似乎會更容易讀懂;倘若沒有,那麼彷彿也無所謂,對的程式碼還是依舊正常的起作用。



雖然有些管理者腦海都有一個大略的印象
- 寫下愈多的註解就愈能夠提昇程式碼的可讀性,他們或許會要求他們的程式員盡可能的撰寫註解,甚至還會訂下某些撰寫的標準,但因為許多管理者本身也不知道看待註解的正確態度,反而訂下許多對程式員來說簡直是莫名其妙的規矩,更加深了程式員原本就對於註解這一類「額外負擔」工作的反感。



在上述的文字中,提到了兩項一般人對於「註解」常有的誤解:(1)註解是程式員額外的負擔,對於工作的完成沒有太多幫助(2)要求程式員撰寫愈多的註解,就愈能確保程式碼的可讀性。



我們所能接觸到程式語言,幾乎都提供了註解的語法,這足以證明註解在程式設計活動中的重要性。而事實上,註解應當被視為是程式的一部份,而非獨立於程式碼之外的組成。在本文中,我們便將探討程式設計活動中這時常被忽略的一環
- 註解。



要更妥善的運用註解,就必須先認識到,註解是在補充程式碼的不足之處,所以程式碼和註解有點互補的關係,當程式碼本身的說明力愈高時,那麼所需的註解也就愈少,反之,所需的註解量也就愈多。之所以需要註解,是因為程式語言語法僅能表達有限的豐富性,需要利用人類的自然語言來進行補充。此外,除了語法本身的表達力受限外,程式員所做的設計、以及撰寫程式碼的風格與方式,都會影響到程式碼的說明力,而當我們在探討註解時,免不了需要探討程式碼本身的說明力。



能自我說明(self-documented)的程式碼正是一種具有高說明力的程式碼。在理想的情況下,具自我說明能力的程式碼單靠程式碼本身,便足以表示出撰寫者期望讀者了解的部份。也就是說,這樣的程式碼毋需為它標上註解,便足以提供極高的說明能力。



因此,註解量究竟多不多,並不是一個關鍵的判斷指標。有時候,寫的糟糕的程式碼反而需要更多的註解。紊亂的結構、不一致又不具意思的變數函式命名、再上混雜在一塊的一堆程式碼,即使搭配大量的註解,閱讀起來想必仍舊令人十分頭痛。當你看到你的程式員在程式碼間穿插著滿滿的註解時,或許反而應該感到更為憂心。



究竟需要多少的註解量,取決於程式碼的品質。良好的程式碼具有良好且一致的命名、排版、風格、以及各式的寫作習慣。而良好的設計,多半都具有簡潔、清晰、易懂的本質。你或許會為你的變數、函式、或類別寫下註解,藉以說明它們的作用。但是,倘若你能為它們取一個好名字,在程式員間共通的隱喻(metaphor)下,這名字將使變數、函式、或類別的作用不言可喻。而註解存在的需要性,也就因為良好的命名而消除。如果你的程式充滿了太多間接、隱畮的設計,那麼你的程式碼便會形同自行搭建起來迷惑讀者的迷宮,需要更多的註解扮演指引圖的作用,才能讓讀者順利從中脫困。相反的,優雅的設計通常本身便充滿了直覺,毋需太多註解補充,便足以充份表達。



有需要重構之症狀的程式碼,多半在進行重構後都能夠提昇程式碼的說明力,例如將一個大型的函式拆解成若干個小型的函式,並賦予其適宜的名稱,將使程式碼更易於明白。你應該盡可能的先提昇程式碼的品質,接著才是輪到註解登場的時機。



許多人為了寫註解而寫註解,他們並不試著明白註解的內容應該寫些什麼。例如,時常看到有些程式員的註解,其實只是程式碼的「換句話說」。註解多此一舉似的表達了讀者讀了程式碼之後也同樣能夠明白的事情。舉個誇張的例子:



double f = d*2; // 將 f 的值設為 d 值的兩倍



「將 f 的值設為 d 值的兩倍」是一般讀者讀了程式碼後即能明白的事實,並不需要額外的註解。在這個誇張的例子中,程式員浪費了時間寫下了註解,但卻沒有為程式增加更多的說明力。你應該利用註解解釋一些單從程式碼看不出來,或者是不易看出來的部份。



程式碼註解應該用來解釋「程式碼為什麼要這麼寫」,而不是「程式碼究竟寫些什麼」。程式碼寫些什麼,是讀者很容易從程式碼本身讀懂的,但讀者不容易在一時之間明白的,卻是你「為什麼」要這麼寫。例如,在一個排序的函式中,它在元素個數小於7時會使用insertion
sort,而在元素個數大於7時則會使用quick sort。你並不需要寫下註解來說明這樣的事情,因為讀者閱讀程式碼時便能明白到這一層。那麼,註解應該寫些什麼呢?像「當元素個數小於7時,insertion
sort運作較快,故使用insertion
sort,反之則使用quick
sort。」這樣的註解,說明了程式碼「為什麼」要這麼寫,而不單只是表面的「寫了些什麼」。



撰寫註解時,有一個很重要的態度便是要站在讀者的立場上來寫。你要設定你所寫註解的對象,並且猜想閱讀者的心,進而利用註解滿足閱讀者的需求。程式碼的讀者可能是和你一同共事的同事、有可能是日後接手你程式碼的人、更有可能是日後的你自己。當你撰寫任何一段程式碼時,你必須持續的在心裡想著「如果閱讀者看到我所寫下的這樣的程式碼,他是否能夠明白這段程式碼想要達成的作用?他是否能夠明白此刻我為什麼要這麼寫?」倘若不行,你是否有其他改寫程式碼的方式,使得讀者能夠明白。倘若你找不到更好的設計或更好的寫作方式,使得程式碼完全能自我說明,那麼就是撰寫註解的時機了,你可以利用註解來補充在程式碼之外想要表達的資訊。



此外,有些文件產生工具可以剖析固定的註解格式,藉以產生相對應的文件。例如javadoc便可以剖析Java原始檔中針對類別及函式的特定註解,並產生出十分實用的API文件。此類的註解,不僅有利於人類讀者閱讀,也有助於自動化產生說明文件。



有些人利用註解來標示日後幾乎無用或暫時無用的程式碼,千萬別這麼做,那會讓你的程式碼太過混亂。也有人在開始撰寫程式碼時發下宏願,立志要讓自己的程式註解看起來漂漂亮亮的,這也請適可而止。雖然註解本身也需要有一致而且清晰的形式,但這不意謂著,你需要花太多心力將它編排的十分美觀。註解本身的撰寫必須不能成為一件負擔太重的工作,因為這會使得你無法持之以恆。



另外,你不應該在註解中寫下一些應該留在版本控制系統上的資訊。例如,修正的記錄。有許多程式員會在程式碼中加上註解,來說明修改的歷程,包括修改日期、修改者、為什麼要修改、修改了什麼等等。這些應該是記錄到版本控制系統上的資訊。此外,當你修改你的程式碼時,請千萬要記得同步修改你的註解,使這二者隨時保持一致。



沒有人永遠能夠完全回到自己撰寫某一段程式碼時的心境,所以也沒有人能夠完全回憶起過去的自己究竟為什麼要這麼的寫下程式碼。基於自己隨時能快速回復記錄,也基於團隊合作時的程式碼共享,撰寫註解絕對是十分重要而且有實際功用的工作。重點不在於寫下多少註解,而是在於註解與程式碼是否能相搭配。好的註解就和好程式碼一樣,不僅形式一致而且清晰。好的註解從閱讀者的立場切入,並且切合閱讀者的心。

No comments: