對程式碼命名的一點思考和理解

湯雪華的部落格發表於2014-09-17

一個軟體最後都會落實到程式碼。而程式碼,其背後的架構設計或設計思想或模式固然重要,但我覺得更重要的東西則是良好的命名。混亂或錯誤的命名不僅讓我們對程式碼難以理解,更糟糕的是,會誤導我們的思維,導致對程式碼的理解完全錯誤。相反,良好的命名,則可以讓我們的程式碼非常容易讀懂,也能向讀者正確表達事物以及邏輯的本質,從而使得程式碼的可維護性就大大增強,讀命名好的文章是非常流暢的,會有一種享受的感覺。

另外一點也許大家還沒感受到,那就是良好的命名,以及良好的命名習慣,由於我們總是對每個概念的名稱要求非常苛刻,我們會思考這個名稱所表達的概念是否正確,該名稱是否正確表達了事物的本質或正確反映了某個行為的邏輯。所以,這種對命名的良好思考習慣,可以反過來幫助我們糾正之前的一些錯誤設計和程式碼實現;比如,你之前有一個地方可能命名不太準確,然後你發現後面有另一個地方需要用這個名字,且更合理。所以你會發現這個名字對前面的地方就不適合了,從而你會去思考前面的地方可能需要用其他的名字,或者你會發現前面的地方的設計根本就是有問題的。這種就是名字可以促使你思考你的設計是否正確的例子。

程式碼命名混亂或錯誤的主要原因:

  1. 沒理解事物的本質;
  2. 理解了事物的本質,但不知道命名的重要性或者根本不屑於做好命名;
  3. 理解了事物的本質,也知道命名的重要性,但沒能力命名好事物;

養成良好的命名習慣的一些想法:

  • 對自己的嚴格自律,自己寫程式碼時要有一種希望把每個名稱都命名好的強烈意識和嚴格的自律意識;
  • 要努力分析和思考當前被你命名的事物或邏輯的本質;這點非常關鍵,思考不深入,就會導致最後對這個事物的命名錯誤,因為你還沒想清楚被你命名的事物是個什麼東西;
  • 在有自律意識和一定的分析能力基礎之上,注意命名的方法技巧;要知道何時用動詞,何時用名詞;以及形容詞放哪裡,動詞放哪裡,名詞放哪裡;也就是小學時的主謂賓要會用;
  • 你的任何一個屬性的名字都要和其實際所代表的含義一致;你的任何一個方法所做的事情都要和該方法的名字的含義一致;
  • 從程式碼的命名可以看出寫程式碼的人程式設計時思路是否清晰,如果你對一個名字的命名不準確,很可能體現出你還沒有理解這個名字背後的東西;
  • 要讓你的程式的每個相似的地方的命名風格總是一致的。不要一會兒大寫,一會兒小寫;一會兒全稱一會兒簡寫;一會兒Pascal命名法,一會兒camel命名法或匈牙利命名法;
  • 不要出現重複的命名;因為通常名稱都有巢狀關係,比如類在名稱空間裡,方法在類裡,所有如果一個概念在名稱空間裡表達了,那就不必再類上再表達一次;
  • 對於屬性或類名,應該總是名詞在最後面,名詞決定了這個屬性代表什麼,前面的部分都是用於修飾這個名詞;比如,假如現在你有一個服務,然後又是一個關於訂單的服務,那就可以命名為OrderService,這樣命名就是告訴我們這是一個服務,然後是一個訂單服務;再比如CancelOrderCommand,看到這個我們就知道這是一個Command,即命令,然後是什麼命令呢?就是一個取消訂單的命令,CancelOrder表示取消訂單;
  • 對於方法,應該總是動詞開頭,名詞結尾;比如Order.AddItem(orderItem);這個,表示訂單類有一個新增訂單項的方法,Add是動詞,表示新增,Item是名詞表示訂單項;
  • 在C#中,我們一般用camel以及Pascal命名法,而不是匈牙利命名法。我覺得主要是兩個原因:1)VS強大的智慧感知提示的存在,我們沒有必要突出變數的型別了,但這個我覺得只是一個次要原因;2)真正的原因,我上面有提到,一個變數,名詞是放在最後的,這個名詞決定了這個變數代表什麼。比如有個變數叫totalCount,我們一看就知道這是一個count,然後count一定是一個int或者long,所以就不需要在強調它的型別了。再比如,remotingRequest, httpRequest,這種,我們也一看就知道他們是請求,一個是remoting的請求,一個是http的請求。remoting,http是用來修飾request的。request決定了這個變數是什麼(同時就意味著我們知道了他的型別了),然後remoting,http這種是進一步說明該request的業務含義或當前上下文。就像disabledButton,我們一看就知道這是一個button,然後是一個什麼Button呢,就是一個已禁用的button。所以,好的名稱,本身就會讓我們很容易知道該名稱是什麼東西,它的型別是什麼,具有什麼業務含義,所以沒有必要再加型別縮寫作為字首;
  • 多學習英文,多看國外優秀開源專案中的命名技巧,會對我們命名有很大幫助;

 

通過一些不太好的程式碼命名來分析一些簡單的命名問題

132318412159759

 

以上程式碼中,有很多問題,我們來一一分析:

  1. 方法的引數,第一個字母,一會兒大寫的P,一會兒小寫的p,不一致;
  2. 第二個引數後面出現多餘的空格,不應該;
  3. _paramsTable這個引數為什麼要出現下劃線,而其他引數沒有下劃線,不一致;
  4. publishRequest屬於camel命名法,而iSignCounter, sStageIsOK這種屬於另一種命名法,這種命名c++中用的多,不一致;
  5. foreach迴圈中,引數名叫instParam,但是後面的集合叫arrParams4SignActions,更對稱一點的,應該叫arrInstParam;
  6. 方法的最後兩行,出現多餘的空格,導致程式碼格式排版混亂;

從上面的程式碼我們可以知道,僅僅是通過這些細節,就能發現很多問題。我們寫程式碼時,只要多細心點,多注意點排版是否美觀一致、命名是否統一,那程式碼寫出來就會漂亮很多了。下面我們再看看其他的程式碼:

132332351682183

 

  1. 上面的程式碼中,兩個引數的命名也不一致,projectid中,i是小寫,但是publishId引數,i卻是大寫,應該都統一為大寫;
  2. ViewData中的key,一會兒是全部大寫的UPDATE,一會兒是另一種命名,不一致;
  3. 上面的兩個紅框標出來的if,雖然都是隻有一行程式碼,但是一個有括號,一個沒有括號,不一致;且第二個if裡出現了多餘的空行,格式混亂;

132340191685294

 

  1. 上面的程式碼中,函式中,一會兒用IList,是一個介面,一會兒用Dictionary,非介面,不一致;應該都用介面,或者都不用介面;
  2. listOriginal和receiverList命名不一致,要麼全部list開頭,要麼全部List結尾;
  3. foreach迴圈中,變數的型別叫TDMSOriginalRequirement,但是變數名卻叫originalItem,而集合名稱又叫listOriginal,應該三者統一;比如foreach (Assembly assembly in assemblies)
  4. +“…”這個地方沒有用空格,加號兩邊應該要空格,這屬於格式混亂,不嚴謹;
  5. createUser這個變數取的很不理想,create是動詞,createUser合起來就是建立使用者的意思,而他這裡要表達的意思是建立人的意思,所以應該叫createdUser或者creator;
  6. 為何originalItemFormat和originalItem的意思可以等價,不合理,如果等價,一開始就要命名為originalItemFormat;而且format是一個東西,動詞放在最後,算個啥?

132358057626942 (1)

 

  1. 上面這個類的幾個私有欄位中,有些帶名稱空間,有些不帶,要麼都不帶,要麼都帶;一般名稱空間都是在上面宣告,後續不需要出現;
  2. ILog logger;這一句有兩個問題:1)logger為何沒有下劃線,不統一;2)為何類名叫ILog,而變數名叫logger,要統一,要麼類名叫ILogger,要麼變數名叫_log;

140002070901351 (1)

 

上面這兩個私有方法,一個是大寫開頭,一個是小寫開頭,不一致,混亂;應該要一致;

總結

通過上面的一些例子,我們知道,在我們不經意間,多寫了一個空格或者一個空行,或者一個字母的大小寫不一致了,都會導致命名的不一致;如果自己沒有養成這種平時注重程式碼命名各種一致性的習慣,那寫出來的程式碼很可能就是像上面那樣。我覺得是非常糟糕的。上面我舉的例子都只是簡單的命名方面的,更深層次的命名問題,比如如何做到名稱和其背後的實現內容一致,這個是需要我們平時不斷修煉的。不是短時間內就可以做到那個程度。

我覺得,要做好命名,歸根結底:

1)先要意識到命名的重要性;2)要端正態度,要認真的寫程式碼;3)要努力推敲每個名稱和其實際做的事情是否一致,也就是命名的準確性;4)要時刻注意命名的各種一致性;

養成良好的命名習慣不是為了別人,不是為了公司,而是為了提高自己的程式設計修養,提高自己認識事物的能力。

相關文章