好的識別字命名,有助於增加程式碼的可讀性、增加程式的自我解釋能力,也因此,衍生出容易維護、容易共享等等的優點。

有趣的是,關於識別字命名法的討論,絕大多數時候,都將重心擺在名稱的結構及形式。不過,關於名稱,另一個議題倒是很少看到有人討論,也就是名稱的長短問題。

多年前,當我開始留意命名慣例時,我會刻意的把每個識別字的名稱都取的工工整整的(姑且不管究竟是用什麼命名法),而且名稱都充份表達出這個名稱的用途。例如:

sdToStockInfoTCPBroadcastingServer

前頭的sd是代表它是個socket,後頭則表示它是連到股票資訊伺服器的TCP廣播伺服器。這麼一來,變數的名稱的確叫人一目了然,一看就大概能夠意會它的作用,但是如果用這種命名的方式來寫程式,那速度可就大受影響了。許多初開始接觸命名慣例的程式設計者,的確會矯正變數名稱不具意義或過短的問題,但往往也矯枉過正,養成了取長名的習慣。

但是,我們會需要為每個名稱(不論是類別、函式、或變數),都取個端端正正,望文生義(可能也十分冗長)的名稱嗎?我覺得不盡然。

可以運用簡稱
名稱最重要的目的,是讓讀者理解。閱讀程式碼時,其實是伴隨著一個context(語境)的。有了這個語境的輔助,其實有些資訊可以省去,但讀者依然能夠理解。這個語境的道理,其實就跟我們一般生活中的文字書寫或對話是相同的。

就好比,今早你的同事問了你一句:「昨天還好吧?」因為你昨天和他一塊喝酒喝到半夜三更,所以你會知道他在問的,多半是你昨天喝多了酒人有沒有怎樣,或者是回家有沒有被太太責備。你的同事不需要說:「昨天喝了那麼多酒,身體有沒有不舒服或回家有沒有被老婆責備?」之類的話。

每一行程式碼,在程式中也都位在某個context之中。而每個識別字名稱所存活的程式碼範圍,也在位於某個context之中。當我們可以用這個名稱所在的context來做為閱讀的輔助時,那麼名稱本身所需要包含的資訊就可以降低。當某個名稱需要包含的資訊可以降低時,它的長度通常就可以縮短。

例如parse一段XML的程式裡有可能是這麼寫:

public class ApplicationParser
{
private Document buildDocument(String filename)
throws Exception
{
XPathFactory xPathFactory =XPathFactory.
newInstance();
XPath xPath = xPathFactory.newXPath();
DocumentBuilderFactory docBuilderFactory =
DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder = docBuilderFactory.
newDocumentBuilder();
return db.parse((File) new File(fileName));
}
}

在這裡我們在一個函式裡取了xPathFactory、xPath、docBuilderFactory、以及docBuilder這四個長名,這四個長名看起來表達了足夠的資訊,應該能增加程式的可讀性。但這麼長的名稱,的確有助於我們多了解一些東西嗎?其實不然。倘若我們這麼寫:

public class ApplicationParser
{
private Document buildDocument(String filename)
throws Exception
{
XPathFactory xpf = XPathFactory.newInstance();
XPath xp = xpf.newXPath();
DocumentBuilderFactory dbf =
DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
return db.parse((File) new File(fileName));
}
}

閱讀到這個函式時,你會因為它們的名稱太短,而無法理解這幾個變數是在做什麼嗎?其實不會,有一個很重要的關鍵,在於這個函式的長度很短,這幾個變數名稱存活的範圍(scope)都很小,而且這個context是確定的,目的就是把fileName所指定的XML檔案建構成一個Document物件。在這個範圍內,我們每種型別的物件,都只用了一個,所以在命名物件時,使用該物件之型別名稱的大寫部份來構成變數的名稱,即足以包含足夠的資訊,例如DocumentBuilderFactory物件命名為dbf。

在上面的例子中,由於函式短小,所以變數名稱也可以予以縮短,卻不致於影響到可讀性,因為這個名稱可以被指涉的範圍,不超過十行,讀者在閱讀時,不會因為變數的名稱很簡短,就無法理解它的作用。但倘若你的函式長度很長(長度長的函式是不好的),那麼變數名稱就不見得能夠加以縮短了,因為一旦變數存在的scope變大,變數名稱所需要包含的資訊量也必須提高。你可以試想,某一個以短名形式定義的變數,它被定義的地點是在你所閱讀的程式碼的一百行以前,當你在一百行之後,再次讀到一個很短、又沒有太多意義的名稱時,你能回想起它實際的作用嗎?恐怕不太容易。

範圍的大小,影響命名的講究程度
過長的識別字名稱也會造成撰寫程式時的困擾。因此,我們並不需要堅持每個識別字都要有個富含意義的冗長名稱,而是,只要這個名稱,在它被指涉的範圍內,具有足夠的意義即可。

而且,因為記憶力的關係,範圍愈大的,名稱所表達出來的意義應該要愈充足。相反的,當一個名稱所能被指涉的範圍愈小時,名稱中所含的意義是否十分完備,就不那麼重要了。因為在一個很小的範圍內,通常不需要很完備的名稱,就足以理解名稱的意義。

所以,一個全域的名稱,例如一個全域變數、一個宣告為public的類別名稱,就必須要比一個區域的變數,在命名上要來得講究。因為全域的名稱,被指涉的範圍是遍及整個程式的每個角落的。你可能在程式碼的任何一個角落看到這個名稱,如果這名稱沒有足夠的意義,那麼想要理解它的實際作用,可能就要花更多的力氣。相反的,一個只存在十行程式碼內的名稱,即便你只是用一個簡單的字母來做為它的名稱,還是可以容易地明白它的意義。

其他影響命名長度的因素
另一方面,在考量名稱長度時,還必須考慮是否存在一些額外的輔助資訊,像是在物件導向程式設計中,類別本身就可以做為函式或屬性名稱的輔助。Java中著名的一個類別Vector,其演化說明了這件事。

最早的Vector,提供了一個叫做addElement()的函式,來讓你將元素加入Vector物件中。這個名稱意義很完整,卻也很冗長。每當你要加入元素時,都得輸入這麼多字,而且程式碼看起來一點也不簡短。後來演化過的版本中,提供了add()函式,作用相同,但名稱變短了。這樣的演化是一個進步。或者你會說,但後來的版本,函式意義沒那麼完備呀?是的,但是,你可以想想,有了Vector這個類別做為輔助的語境,當你呼叫Vector物件的add()時,除了加入「元素」之外,還能加入什麼呢?顯然「Element」是累贅的,使用add(),足以表示「加入元素」這個概念。

重視命名慣例是好的習慣,命名慣例是程式撰寫者與閱讀者的metaphor,可以加強溝通的效率及效果。但在命名上也不可矯枉過正,把名稱取得冗長異常,可以視名稱所存在的範圍大小,還有輔助的語境,來決定它應包含的資訊量,藉以縮短名稱的長度。

 

專欄作者


熱門新聞

Advertisement