<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Clean Code - 有意義的命名

          共 2696字,需瀏覽 6分鐘

           ·

          2022-03-19 10:33

          最近生活工作趨于穩(wěn)定,所以決定重新看一遍《代碼整潔之道》,記錄一些讀書摘要,便于記憶。

          在軟件開發(fā)過程中,隨處可見命名。我們給變量函數(shù)、參數(shù)封包命名,我們給源代碼源代碼所在目錄命名,我們給jar文件、war文件ear文件命名。

          一個(gè)好的命名,能夠顯著提高代碼的可閱讀性,讓人一看就知道這段代碼是什么意思,下面列出了取個(gè)好名字的幾條簡單規(guī)則。

          1、名副其實(shí)

          一個(gè)好的名稱,可以讓我們看到這個(gè)變量、函數(shù)或類的時(shí)候,立馬就能夠知道它是干嘛的。

          一個(gè)好的名稱應(yīng)該可以告訴你,這個(gè)變量、函數(shù)或類為什么會存在,做了什么事,應(yīng)該怎么用。

          如果名稱需要注釋來補(bǔ)充,那就不算是名副其實(shí)。

          此外,如果一旦發(fā)現(xiàn)有更好的名稱,就立馬換掉舊的。

          示例一

          反例

          int?d;?//?消逝的時(shí)間,以日計(jì)

          名稱 d 什么也沒說明,如果沒有注釋,都不知道是干啥的,就像你定義一個(gè)變量 i,沒有注釋,誰知道它是干啥的。d 這個(gè)名稱并沒有引起對時(shí)間消逝的感覺,跟別說以日計(jì)了。我們應(yīng)該選擇指明了計(jì)量對象和計(jì)量單位的名稱。

          正例

          int?elapsedTimeInDays;
          int?daysSinceCreation;
          int?daysSinceModification;

          使用 elapsedTimeDays 代替上面的 d,elapsedTimee 指明了計(jì)量對象,即消逝的時(shí)間,InDays 指明了計(jì)量單位,以日計(jì)算。一看這個(gè)變量,就知道它是干嘛的,一目了然,根本不需要注釋。

          示例二

          反例

          public?List<int[]>?getThem(){
          ??List<int[]>?list1?=?new?ArrayList<int[]>();
          ??for(int[]?x?:?theList)
          ????if(x[0]?==?4)
          ??????list1.add(x);
          ??return?list1;
          }

          上面這段代碼,誰能看出來它要做什么事(我想哪怕是創(chuàng)作者本人,時(shí)間久了,他也不知道這段代碼是要干嘛了)。

          這段代碼并不復(fù)雜,沒有復(fù)雜的表達(dá)式,只有三個(gè)變量和兩個(gè)常量,那為什么這么難懂呢?原因就是它太模糊了。

          (1)theList 中是什么類型的東西?
          (2)theList 零下標(biāo)條目(x[0] 中的 0)的意義是什么?
          (3)值 4 的意義是什么?
          (4)我怎么使用返回的列表?

          正例

          上面這段代碼其實(shí)是一個(gè)掃雷游戲的一個(gè)功能點(diǎn),即收集掃雷盤面上被標(biāo)記的單元格,但是由于這四個(gè)模糊點(diǎn),導(dǎo)致我們無法看懂這段代碼到底要干嘛。下面我們就針對這四點(diǎn),對這段代碼做一下修改,使它的意圖能夠清晰的暴漏出來。

          首先根據(jù)功能點(diǎn)的描述,將函數(shù)名修改為 getFlaggedCells,list1 是一個(gè)集合,里面裝著被標(biāo)記的單元格,我們將其改名為 flaggedCells。

          掃雷盤面是名為 theList 的單元格列表,我們將其改名為 gameBoard,int 數(shù)組表示盤面上的每個(gè)單元格,我們將其改名為 cell。

          零下標(biāo)條目是一種狀態(tài)值,我們創(chuàng)建一個(gè)常量 STATUS_VALUE,用這個(gè)常量代替魔法數(shù) 0。狀態(tài)值 4 表示“已標(biāo)記”,我們創(chuàng)建一個(gè)常量 FLAGGED,用這個(gè)常量代替魔法數(shù) 4。

          經(jīng)過上面對命名的修改,代碼變得明確多了。

          static?final?int?STATUS_VALUE?=?0;
          static?final?int?FLAGGED?=?4;

          public?List<int[]>?getFlaggedCells(){
          ????List<int[]>?flaggedCells?=?new?ArrayList<int[]>();
          ????for(int[]?cell?:?gameBoard)
          ??????if(cell[STATUS_VALUE]?==?FLAGGED)
          ????????flaggedCells.add(cell);
          ????return?flaggedCells;
          }

          對于上述代碼,還可以更進(jìn)一步優(yōu)化,使其意義更加明確。我們不再用 int 數(shù)組表示單元格,而是另寫一個(gè)類 Cell,Cell 包含一個(gè)函數(shù) isFlagged,使用這個(gè)函數(shù)替換上面代碼中的邏輯判斷,可以使代碼的意義更加明確。

          public?class?Cell{
          ??public?Boolean?isFlagged(){
          ????List<int[]>?flaggedCells?=?new?ArrayList<int[]>();
          ????for(Cell?cell?:?gameBoard)
          ??????if(cell.isFlagged())
          ????????flaggedCells.add(cell);
          ????return?flaggedCells;
          ??}
          }

          2、避免誤導(dǎo)

          避免留下掩藏代碼本意的錯(cuò)誤線索;避免使用與本意相悖的詞。我們舉幾個(gè)例子來說明一下。

          示例一

          即便容器就是個(gè) List 或者 Map,最好也別在名稱中寫出容器類型名

          別用 accountList 來指稱一組賬號,除非它真的是 List 類型,因?yàn)?List、Map 等都是 Java 中容器的類型,有特殊的含義,如果包含賬號的容器不是個(gè) List,就會引起錯(cuò)誤的判斷。

          所以用 accountGroup 或 bunchOfAccounts,甚至直接用 accounts 都會好一些。

          示例二

          hp、aix 和 sco 都不該用來做變量名,因?yàn)樗鼈兌际?UNIX 操作系統(tǒng)的專有名詞。

          當(dāng)我們編寫三角計(jì)算程序時(shí),hp 看起來是個(gè)很好的縮寫(hypotenuse,斜邊),但是也不要使用。

          示例三

          提防使用不同之處較小的名稱。

          XYZControllerForEfficientHandlingOfStrings 和 XYZControllerForEfficientStorageOfStrings,這是兩個(gè)很長的變量,除了中間 Handling 和 Storage 這很短的一部分不同外,其他完全相同,使用的時(shí)候需要看很長時(shí)間,才能區(qū)分出來,如果使用快捷鍵,很容易就弄錯(cuò)了。

          示例四

          不要使用小寫字母 l 和大寫字母 O 作為變量名,組合使用也不可以,原因在于它們看起來太像阿拉伯?dāng)?shù)字 1 和 0 了,很容易就搞混了。

          3、做有意義的區(qū)分

          對于不同含義的變量、函數(shù)或者類,在命名的時(shí)候,不光要能夠被編譯器區(qū)分出來,更應(yīng)該讓讀者能夠區(qū)分出來。我們舉幾個(gè)例子來說明一下。

          示例一

          通過添加數(shù)字來進(jìn)行區(qū)分,這只能讓編譯器滿意,但對于程序員來說,只會降低代碼的可讀性以及后期的可維護(hù)性。

          public?static?void?copyChars(char?a1[],?char?a2[]){
          ??for(int?i?=?0;?i?????a2[i]?=?a1[i];
          ??}
          }

          見上面這個(gè)例子,a1 和 a2 對于編譯器來說,它可以分辨出來這是兩個(gè)變量,但是這種用數(shù)字進(jìn)行區(qū)分的方式,對于程序員來說,根本不知道這二者有何不同啊。

          觀察上面的代碼,函數(shù)名是 copyChars,那么這個(gè)函數(shù)的功能就是實(shí)現(xiàn)字符的復(fù)制。既然是復(fù)制,就會有源參數(shù)和目標(biāo)參數(shù),所以我們可以將參數(shù)名改為 source 和 destination ,這樣的話,這個(gè)函數(shù)的含義將會更加清晰。

          public?static?void?copyChars(char?source[],?char?destination[]){
          ??for(int?i?=?0;?i?????destination[i]?=?source[i];
          ??}
          }
          示例二

          廢話是另一種沒有意義的區(qū)分。舉個(gè)例子,假設(shè)你有一個(gè) Product 類,如果還有一個(gè) ProductData 或者 ProductInfo 類,它們的名稱雖然不同,意思卻無區(qū)別。Data 和 Info,就像英文中的 a、an 和 the 一樣,是意義含混的廢話。

          4、使用讀得出來的名稱

          在命名的時(shí)候,不要用傻乎乎的自造詞,最好使用恰當(dāng)?shù)挠⒄Z單詞。這樣做,既可以提高代碼的可讀性,也可以提高團(tuán)隊(duì)內(nèi)部的溝通效率。

          反例

          public?class?DtaRcrd102{
          ??private?Date?genymdhms;
          ??private?Date?modymdhms;
          ??private?final?String?pszqint?=?"102";
          ??/*...*/
          }

          正例

          public?class?Customer{
          ??private?Date?generationTimestamp;
          ??private?Date?modificationTimestamp;
          ??private?final?String?recordId?=?"102";
          ??/*...*/
          }

          5、使用可搜索的名稱

          長名稱勝于短名稱,因?yàn)槎堂Q很難在一大段代碼中找出來。

          示例一

          找 MAX_CLASSES_PER_STUDENT 很容易,但找數(shù)字 7 就麻煩了,它可能是某些文件名或其他常量定義的一部分,出現(xiàn)在因不同意圖而采用的表達(dá)式中。

          示例二

          e 也不是一個(gè)便于搜索的好變量名,它是英文中最常用的字母,在每個(gè)程序、每段代碼中都可能出現(xiàn)。

          示例三

          反例

          for(int?j?=?0;?j?34;?j++){
          ??s?+=?(t[j]?*?4)?/?5;
          }

          首先看一下反例所示的代碼片段,它使用了一些很短且沒有意義的名稱(s、t),還有一些魔法數(shù)(34、4、5),拿到這段代碼的人根本無法猜出這段代碼要干啥。

          正例

          int?realDaysPerIdealDay?=?4;
          static?final?int?WORK_DAYS_PER_WEEK?=?5;
          int?sum?=?0;
          for(int?j?=?0;?j???int?realTaskDays?=?taskEstimate[j]?*?realDaysPerIdealDay;
          ??int?realTaskWeeks?=?realTaskDays?/?WORK_DAYS_PER_WEEK;
          ??sum?+=?realTaskWeeks;
          }

          再看正例所示的代碼片段,使用命名清晰的英文單詞代替了這幾個(gè)變量,使用長名稱代替了這幾個(gè)常量,通過這些修改,代碼就像文章一樣,能夠讀出它的含義,這才是一段好代碼。

          采用能表達(dá)意圖的名稱 WORK_DAYS_PER_WEEK 替換魔法數(shù) 5,貌似拉長了代碼,但利遠(yuǎn)遠(yuǎn)大于弊,能更加清楚表達(dá)作者的意圖。

          6、避免使用編碼

          規(guī)則一:避免把類型和作用域編進(jìn)名稱里面。

          反例

          String?phoneNumberString;

          正例

          String?phoneNumber;
          規(guī)則二:不必使用 m_ 前綴來標(biāo)明成員變量。

          根據(jù)多年的開發(fā)習(xí)慣,在讀代碼的時(shí)候,我們很容易無視前綴(或后綴),只看名稱中有意義的部分,代碼都地越多,眼中就越?jīng)]有前綴,最終,前綴變成了不入眼的廢料,變成了舊代碼的標(biāo)志物。

          反例

          public?class?Part{
          ??private?String?m_dsc;
          ??void?setName(String?name){
          ????m_dsc?=?name;
          ??}
          }

          正例

          public?class?Part{
          ??private?String?description;
          ??void?setDescription(String?description){
          ????this.description?=?description;
          ??}
          }

          7、避免思維映射

          不應(yīng)當(dāng)讓讀者在腦中把你的名稱翻譯為他們熟知的名稱。

          示例一:單字母變量名就是個(gè)問題,比如循環(huán)計(jì)數(shù)器避免使用字母 i、j、k,比如避免使用字母 a、b、c 進(jìn)行命名。

          專業(yè)程序員了解“明確是王道”,能夠編寫其他人能理解的代碼。

          8、類名和方法名

          規(guī)則一:類名和對象名應(yīng)該是名詞或名詞短語。

          舉例,應(yīng)該使用 Customer、WikiPage、Account 和 AddressParser 這樣的類名,避免使用 Manager、Processor、Data 或 Info 這樣的類名。

          規(guī)則二:類名不應(yīng)當(dāng)是動(dòng)詞。
          規(guī)則三:方法名應(yīng)當(dāng)是動(dòng)詞或動(dòng)詞短語。

          9、每個(gè)概念對應(yīng)一個(gè)詞

          舉個(gè)栗子,fetch、retrieve 和 get 都有獲取的意思,如果代碼中有多處需要使用獲取這個(gè)詞,最好都使用其中的一個(gè),而不是一個(gè)地方用這個(gè),一個(gè)地方用另一個(gè),這不是一個(gè)很規(guī)范的代碼。

          反例

          public?String?getName(){
          ??/*...*/
          }

          public?String?fetchAddress(){
          ??/*...*/
          }

          正例

          public?String?getName(){
          ??/*...*/
          }

          public?String?getAddress(){
          ??/*...*/
          }

          10、別用雙關(guān)語

          避免將同一單詞用于不同的目的。

          反例

          public?List?addXXX(String?name){
          ??names.add(name);
          ??return?names;
          }

          public?int?addXXX(int?x,?int?y){
          ??int?z?=?x?+?y;
          ??return?z;
          }

          見反例程序,第一個(gè) add 方法的功能是將某個(gè)元素添加到集合中,第二個(gè) add 方法的功能是實(shí)現(xiàn)兩個(gè)數(shù)值的相加。同一個(gè)單詞有了兩種不同的含義,這就是雙關(guān)語,雙關(guān)語很容易使人混淆,當(dāng)我們讀這段代碼的時(shí)候,看到兩個(gè) add ,很可能認(rèn)為二者都是一樣的意思。

          正例

          public?List?insert(String?name){
          ??names.add(name);
          ??return?names;
          }

          public?int?add(int?x,?int?y){
          ??int?z?=?x?+?y;
          ??return?z;
          }

          見正例程序,我們把第一個(gè) add 方法改成了 insert(或者append),這樣代碼一下子就簡潔明了起來,我們一看代碼就知道,insert 是添加的意思,add 是相加的意思,這才是易于理解的代碼嘛。

          11、使用解決方案領(lǐng)域名稱

          記住,只有程序員才會讀你的代碼,所以,盡管用那些計(jì)算機(jī)科學(xué)術(shù)語、算法名、模式名、數(shù)學(xué)術(shù)語吧。

          舉個(gè)栗子,如果使用的是訪問者(Vistor)模式,名稱 AccountVistor 富有意義,如果創(chuàng)建的是一個(gè)隊(duì)列,使用 JobQueue 命名,則會更加清晰。

          12、使用源自所涉問題領(lǐng)域的名稱

          如果不能用程序員熟悉的術(shù)語來給手頭的工作命名,就采用從所涉問題領(lǐng)域而來的名稱吧。

          13、添加有意義的語境

          很少有名稱是能自我說明的--多數(shù)都不能。反之,如果你需要用有良好命名的類、函數(shù)或名稱空間來放置名稱,給讀者提供語境。

          舉個(gè)栗子,假設(shè)你有名為 firstName、lastName、street(街道)、houseNumber(門牌號)、city(城市)、state(州)和 zipcode(郵政編碼)這幾個(gè)變量,當(dāng)它們擱在一塊兒的時(shí)候,很明確是構(gòu)成一個(gè)地址。如果只是在某個(gè)地方看見孤零零一個(gè) state 變量,你還能理所當(dāng)然地推斷出那是某個(gè)地址的一個(gè)部分嘛,畢竟 state 經(jīng)常被我們用來描述狀態(tài)。

          那應(yīng)該怎么辦呢?就像上面說的,哪怕在某處只看到一個(gè) state 變量,也能很快猜出它是某個(gè)地址的一部分呢?有兩個(gè)方法,描述如下。

          一是添加前綴,比如 addrFirstName、addrLastName、addrState,以此來提供語境。當(dāng)我們哪怕只看一個(gè) addrState 變量,根據(jù)前綴 addr 馬上就可以知道它是隸屬于某個(gè)地址的一部分。

          二是將這些變量封裝到一個(gè) JavaBean 中。比如上面那些變量,我們可以創(chuàng)建一個(gè) Address 類,然后將這些變量作為它的屬性。

          public?class?Address{
          ??private?String?firstName;
          ??private?String?lastName;
          ??private?String?street;
          ??private?String?houseNumber;
          ??private?String?city;
          ??private?String?state;
          ??private?String?zipcode;
          }

          14、不要添加沒用的語境

          只要短名稱足夠清楚,就要比長名稱好。別給名稱添加不必要的語境。

          舉個(gè)栗子,假設(shè)有一個(gè)名為“加油站”(Gas Station)的應(yīng)用,如果在其中給每個(gè)類都添加 GSD 前綴就沒有必要了,有兩個(gè)弊端,如下所述。

          一是不利于搜索,當(dāng)你想要搜索某個(gè)包含 G 的類(或者變量、方法)時(shí),你輸入 G,按下自動(dòng)完成鍵,結(jié)果會得到系統(tǒng)中全部類的列表,這樣搞得 IDEA 的搜索功能也失效了呀。

          二是代碼冗余,假設(shè)有一個(gè)地址類,命名為 Address 就已經(jīng)很清晰了,如果你偏偏要命名為 GASAddress,前三個(gè)字母純屬多余,何必呢?


          瀏覽 71
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  亚洲AV永久无码精品久久麻豆 | 成人黄色A片 | 牛牛影视一区二区 | 成人午夜精品视频在线观看 | 精品无码秘 人妻一区二区三区 |