本文與大家聊一聊編程中非常關(guān)鍵的一個點,如何更好的對代碼命名。 一、引言 《代碼整潔之道》這本書提出了一個觀點:代碼質(zhì)量與其整潔度成正比,干凈的代碼,既在質(zhì)量上可靠,也為后期維護、升級奠定了良好基礎(chǔ)。書中介紹的規(guī)則均來自作者多年的實踐經(jīng)驗,涵蓋從命名到重構(gòu)的多個編程方面,雖為一“家”之言,然誠有可資借鑒的價值。 但我們知道,很多時候,理想很豐滿,現(xiàn)實很骨感,也知道人在江湖,身不由己。因為項目的緊迫性,需求的多樣性,我們無法時時刻刻都寫出整潔的代碼,保持自己輸出的都是高質(zhì)量、優(yōu)雅的代碼。 但若我們理解了代碼整潔之道的精髓,我們會知道怎樣讓自己的代碼更加優(yōu)雅、整潔、易讀、易擴展,知道真正整潔的代碼應(yīng)該是怎么樣的,也許就會漸漸養(yǎng)成持續(xù)輸出整潔代碼的習(xí)慣。 而且或許你會發(fā)現(xiàn),若你一直保持輸出整潔代碼的習(xí)慣,長期來看,會讓你的整體效率和代碼質(zhì)量大大提升。 二、本文涉及知識點思維導(dǎo)圖 先放出這篇文章所涉及內(nèi)容知識點的一張思維導(dǎo)圖,就開始正文。大家若是疲于閱讀文章正文,直接看這張圖,也是可以Get到本文的主要知識點的大概。 三、高質(zhì)量代碼的命名法則 1 名副其實 名副其實說起來貌似很簡單,但真正做起來,似乎沒那么容易。選個好名字要花一些時間,但其實選好名字之后省下來的時間,要比之前選名字時花掉的時間多得多。 我們一旦發(fā)現(xiàn)有更好的名稱時,就應(yīng)該換掉之前的舊名稱,這樣做讀你代碼的人(包括你自己),都會很開心。 一個好的變量、函數(shù)或類的名稱應(yīng)該已經(jīng)幾乎答復(fù)了所有的大問題。它應(yīng)該告訴你,這個名稱所代表的內(nèi)容,為什么會存在,做了什么事情,應(yīng)該如何用等。 如果一個名稱需要注釋來補充才能讓大家明白其真正含義,那其實就不算是名副其實。(并不是說不需要注釋,恰如其分的注釋是程序員讓自己代碼錦上添花的好方法,關(guān)于注釋的一些注意事項,稍后會有文章專門涉及。) 舉個栗子: 以下的這句代碼里的d就不算是個好命名。名稱d什么都沒說,它沒引起我們對時間消逝的感覺,更別說單位是天了: [cpp] view plain copy print? int d; // elapsed time in days||經(jīng)過了幾天時間 我們應(yīng)該選擇這樣的指明了計量對象和計量單位的名稱: [cpp] view plain copy print? int elapsedTimeInDays; int daysSinceCreation; int daysSinceModification; int fileAgeInDays; 2 避免造成誤導(dǎo) 我們應(yīng)該避免留下隱藏代碼本意的錯誤線索,也應(yīng)該避免使用與本意相悖的詞。例如,別用accountList來指一組賬號,除非它真的是List類型,用accountGroup、bunchOfAccounts,或者直接用accounts,都是更好的選擇。 盡量提防長得太像的名稱。想?yún)^(qū)分XYZControllerForEfficientHandlingOfStrings和XYZControllerForEfficientStorageOfStrings,會花費我們太多的時間。因為這兩個詞,實在太相似了。 以同樣的方式拼寫出同樣的概念才是信息,拼寫前后不一致就是誤導(dǎo)。 3 盡量做有意義的區(qū)分 1.盡量避免使用數(shù)字系列命名(a1、a2…….aN)。這樣的名稱純屬誤導(dǎo),因為很多時候完全沒有提供正確的信息,沒有提供導(dǎo)向作者意圖的線索。 2.廢話是另一種沒有意義的區(qū)分。如果我們有一個Product類,還有一個ProductInfo或ProductData類,那么他們的名稱雖然不同,但意思卻無區(qū)別。這里的Info、Data就像a、an和the一樣,是意義含混的廢話。 注意,只要體現(xiàn)出有意義的區(qū)分,使用a、the這樣的前綴就沒錯。例如,將a用在域內(nèi)變量,把the用于函數(shù)參數(shù)。 4 盡量使用讀得出來的名稱 我們要使用讀得出來的名稱。如果名稱讀不出來,討論的時候就會不方便且很尷尬,甚至讓旁人覺得很蠢。 例如,變量名稱是beeceearrthreecee,討論的時候讀起來簡直像沒吃藥。 5 盡量使用可搜索的名稱 單字母和數(shù)字常量有個問題,就是很難再一大篇文字中找出來。 找MAX_CLASSED_PER_STUDENT很容易,但想找數(shù)字7,就很麻煩。 同樣,字母e也不是個便于搜索的好變量名。因為作為英文中最常用的字母,在每個程序、每段代碼中都有可能出現(xiàn)。 名稱長短應(yīng)與其作用域大小相對應(yīng),若變量或常量可能在代碼中多處使用,應(yīng)賦予其以便于搜索的名稱。 舉個栗子,比較如下兩段代碼: [cpp] view plain copy print? for (int j=0; j<34; j++) { s += (t[j]*4)/5; } 和 [cpp] view plain copy print? const int WORK_DAYS_PER_WEEK = 5; int sum = 0; for (int j=0; j < NUMBER_OF_TASKS; j++) { int realTaskDays = taskEstimate[j] * realDaysPerIdealDay; int realTaskWeeks = (realdays / WORK_DAYS_PER_WEEK); sum += realTaskWeeks; } 按整潔代碼的要求來評判,第一段代碼會讓讀者不知所云,第二段代碼比第一段好太多。第二段代碼中,sum并非特別有用的名稱,但至少他搜索得到。采用能表達(dá)意圖的名稱,貌似拉長了函數(shù)代碼,但要想想看,WORK_DAYS_PER_WEEK要比數(shù)字5好找得多,而列表中也只剩下了體現(xiàn)我們意圖的名稱。 6 取名不要繞彎子 我們?nèi)∶臅r候要避免思維映射,不應(yīng)當(dāng)讓讀者在腦中把你的名稱翻譯為他們熟知的名稱,也就是說取名不要繞彎子,而是要直白,直截了當(dāng)。 在多數(shù)情況下,單字母不是個好的命名選擇,除非是在作用域小、沒有名稱沖突的地方,比如循環(huán)。循環(huán)計數(shù)器自然有可能被命名為i,j或k(最好別用字母l),這是因為傳統(tǒng)上我們慣用單字母名稱做循環(huán)計數(shù)器。 程序員通常都是聰明人,聰明人有時會借助腦筋急轉(zhuǎn)彎炫耀其聰明。而聰明程序員和專業(yè)程序員之間的區(qū)別在于,專業(yè)程序員了解,明確就是王道。專業(yè)的程序員善用其能,能編寫出其他人能理解的代碼。 7 類名盡量用名詞 類名盡量用名詞或名詞短語,比如Customer, WikiPage,Account, 或 AddressParser。 類名最好不要是動詞。 8 方法名盡量用動詞 方法名盡量用動詞或動詞短語。比如postPayment, deletePage, 或者save。 屬性訪問器、修改器和斷言應(yīng)該根據(jù)其value來命名,并根據(jù)標(biāo)準(zhǔn)加上get、set和is前綴。 舉個栗子,這里的getName、setName等命名都很OK: [cpp] view plain copy print? string name = employee.getName(); customer.setName("mike"); if (paycheck.isPosted())... 而重載構(gòu)造器時,使用描述了參數(shù)的靜態(tài)工廠方法名。如: [cpp] view plain copy print? Complex fulcrumPoint =Complex.FromRealNumber(666.0); 通常好于: [cpp] view plain copy print? Complex fulcrumPoint = new Complex(666.0); 我們也可以考慮將相應(yīng)的構(gòu)造器設(shè)置為private,強制使用這種命名手段。 9 每個概念對應(yīng)一詞,并一以貫之 我們需給每個概念選一個詞,并且一以貫之。 例如,使用fetch、retrieve和get來給在多個類中的同種方法命名,你怎么記得住哪個類中是哪個方法呢? 同樣,在同一堆代碼中混用controller、manager,driver,就會令人困惑。DeviceManager和Protocol-Controller之間有何根本區(qū)別?為什么不全用controller或者manager?他們都是Driver嗎?這就會讓讀者以為這兩個對象是不同的類型,也分屬不同的類。 所以,對于那些會用到你代碼的程序員,一以貫之的命名法簡直就是天降福音。 10 通俗易懂 我們應(yīng)盡力寫出易于理解的變量名,把代碼寫得讓別人能一目了然,而不必讓人去非常費力地去揣摩其含義。我們想要那種大眾化的作者盡責(zé)地寫清楚的通俗易懂的暢銷書風(fēng)格,而不是那種學(xué)者學(xué)院風(fēng)的晦澀論文寫作風(fēng)格。 11 盡情使用解決方案領(lǐng)域?qū)I(yè)術(shù)語 記住,只有程序員才會讀你寫的代碼。所以,盡管去用那些計算機科學(xué)(Computer Science,CS)領(lǐng)域的專業(yè)術(shù)語、算法名、模式名、數(shù)學(xué)術(shù)語。 對于熟悉訪問者(Visitor)模式的程序來說,名稱AccountVisitor富有意義。給技術(shù)性的事物取個恰如其分的技術(shù)性名稱,通常就是最靠譜的做法。 12 添加有意義的語境 很少有名稱是可以自我說明的。所以,我們需要用有良好命名的類,函數(shù)或名稱空間來放置名稱,給讀者提供語境。若沒能提供放置的地方,還可以給名稱添加前綴。 舉個栗子,假如我們有名為firstName、lastName、street、houseNumber、city、state和zipcode的變量。當(dāng)他們擱一塊兒的時候,的確是構(gòu)成了一個地址。不過,假如只是在某個方法中看到一個孤零零的state呢?我們會推斷這個變量是地址的一部分嗎? 我們可以添加前綴addrFirstName、addrLastName、addrState等,以此提供語境。至少,讀者可以知道這些變量是某個更大變量的一部分。當(dāng)然,更好的方案是創(chuàng)建名為Address的類。這樣,即便是編譯器也會知道這些變量是隸屬于某個更大的概念了。 另外,只要短名稱足夠好,對含義的表達(dá)足夠清除,就要比長名稱更合適。添加有意義的語境甚好,別給名稱添加不必要的語境。 四、小結(jié) 其實,取一個好名字最難的地方在于需要良好的描述技巧和共有的文化背景。與其說這是一種技術(shù)、商業(yè)或管理問題,還不如說這是一種教學(xué)問題。 不妨試試上面列出的這十二條規(guī)則與要點,看看你的代碼可讀性是否有所提升。而如果你是在維護別人的代碼,或者是在重構(gòu),效果應(yīng)該會是立竿見影的。 五、本文涉及知識點提煉整理 文章開頭部分已經(jīng)用思維導(dǎo)圖的方式展現(xiàn)了本文的知識點,這邊再貼出一個文字列表版,方便大家整理: 要點一:要名副其實。一個好的變量、函數(shù)或類的名稱應(yīng)該已經(jīng)答復(fù)了所有的大問題。一個好名稱可以大概告訴你這個名稱所代表的內(nèi)容,為什么會存在,做了什么事情,應(yīng)該如何用等。 要點二:要避免誤導(dǎo)。我們應(yīng)該避免留下隱藏代碼本意的錯誤線索,也應(yīng)該避免使用與本意相悖的詞。 要點三:盡量做有意義的區(qū)分。盡量避免使用數(shù)字系列命名(a1、a2…….aN)和沒有意義的區(qū)分。 要點四:盡量使用讀得出來的名稱。如名稱讀不出來,討論的時候會不方便且很尷尬。 要點五:盡量使用可搜索的名稱。名稱長短應(yīng)與其作用域大小相對應(yīng),若變量或常量可能在代碼中多處使用,應(yīng)賦予其以便于搜索的名稱。 要點六:取名不要繞彎子。取名要直白,要直截了當(dāng),明確就是王道。 要點七:類名盡量用名詞。類名盡量用名詞或名詞短語,最好不要是動詞。 要點八:方法名盡量用動詞。方法名盡量用動詞或動詞短語。 要點九:每個概念對應(yīng)一詞,并一以貫之。對于那些會用到你代碼的程序員,一以貫之的命名法簡直就是天降福音。 要點十:通俗易懂。應(yīng)盡力寫出易于理解的變量名,要把代碼寫得讓別人能一目了然,而不必讓人去非常費力地去揣摩其含義。 要點十一:盡情使用解決方案領(lǐng)域?qū)I(yè)術(shù)語。盡管去用那些計算機科學(xué)領(lǐng)域的專業(yè)術(shù)語、算法名、模式名、數(shù)學(xué)術(shù)語。 要點十二:要添加有意義的語境。需要用有良好命名的類,函數(shù)或名稱空間來放置名稱,給讀者提供語境。若沒能提供放置的地方,還可以給名稱添加前綴。 |