精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

如何寫(xiě)出健壯的代碼?

開(kāi)發(fā) 開(kāi)發(fā)工具
關(guān)于代碼的健壯性,其重要性不言而喻。那么如何才能寫(xiě)出健壯的代碼?阿里文娛技術(shù)專家長(zhǎng)統(tǒng)將從防御式編程、如何正確使用異常和 DRY 原則等三個(gè)方面,并結(jié)合代碼實(shí)例,分享自己的看法心得,希望對(duì)同學(xué)們有所啟發(fā)。

[[333767]]

關(guān)于代碼的健壯性,其重要性不言而喻。那么如何才能寫(xiě)出健壯的代碼?阿里文娛技術(shù)專家長(zhǎng)統(tǒng)將從防御式編程、如何正確使用異常和 DRY 原則等三個(gè)方面,并結(jié)合代碼實(shí)例,分享自己的看法心得,希望對(duì)同學(xué)們有所啟發(fā)。

你不可能寫(xiě)出完美的軟件。因?yàn)樗辉霈F(xiàn),也不會(huì)出現(xiàn)。

每一個(gè)司機(jī)都認(rèn)為自己是最好的司機(jī),我們?cè)诒梢暷切╆J紅燈、亂停車、胡亂變道不遵守規(guī)則的司機(jī)同時(shí),更應(yīng)該在行駛的過(guò)程中防衛(wèi)性的駕駛,小心那些突然沖出來(lái)的車輛,在他們給我們?cè)斐陕闊┑臅r(shí)候避開(kāi)他。這跟編程有極高的相似性,我們?cè)诔绦虻氖澜缋镆粩嗟母说拇a接合(那些不符合你的高標(biāo)準(zhǔn)的代碼),并處理可能有效也可能無(wú)效的輸入。無(wú)效的的輸入就好像一輛橫沖直撞的大卡車,這樣的世界防御式編程也是必須的,但要駛得萬(wàn)年船我們可能連自己都不能信任,因?yàn)槟悴恢罌_出去的那輛是不是你自己的車。關(guān)于這點(diǎn)我們將在防御式編程中討論。

沒(méi)人能否認(rèn)異常處理在 Java 中的重要性,但如果不能正確使用異常處理那么它帶來(lái)的危害可能比好處更多。我將在正確使用異常中討論這個(gè)問(wèn)題。

DRY,Don't Repeat Yourself. 不要重復(fù)你自己。我們都知道重復(fù)的危害性,但重復(fù)時(shí)常還會(huì)出現(xiàn)在我們的工作中、代碼中、文檔中。有時(shí)重復(fù)感覺(jué)上是不得不這么做,有時(shí)你并沒(méi)有意識(shí)到是在重復(fù),有時(shí)卻是因?yàn)閼卸瓒貜?fù)。

好借好還再借不難。這句俗話在編程世界中同樣也是至理名言。只要在編程,我們都要管理資源:內(nèi)存、事物、線程、文件、定時(shí)器,所有數(shù)量有限的事物都稱為資源。資源使用一般遵循的模式:你分配、你使用、你回收。

防御式編程

防御式編程是提高軟件質(zhì)量技術(shù)的有益輔助手段。防御式編程的主要思想是:程序/方法不應(yīng)該因傳入錯(cuò)誤數(shù)據(jù)而被破壞,哪怕是其他由自己編寫(xiě)方法和程序產(chǎn)生的錯(cuò)誤數(shù)據(jù)。這種思想是將可能出現(xiàn)的錯(cuò)誤造成的影響控制在有限的范圍內(nèi)。

一個(gè)好程序,在非法輸入的情況下,要么什么都不輸出,要么輸出錯(cuò)誤信息。我們往往會(huì)檢查每一個(gè)外部的輸入(一切外部數(shù)據(jù)輸入,包括但不僅限于數(shù)據(jù)庫(kù)和配置中心),我們往往也會(huì)檢查每一個(gè)方法的入?yún)ⅰN覀円坏┌l(fā)現(xiàn)非法輸入,根據(jù)防御式編程的思想一開(kāi)始就不引入錯(cuò)誤。

使用衛(wèi)語(yǔ)句

對(duì)于非法輸入的檢查我們通常會(huì)使用 if…else 去做判斷,但往往在判斷過(guò)程中由于參數(shù)對(duì)象的層次結(jié)構(gòu)會(huì)一層一層展開(kāi)判斷。

  1. public void doSomething(DomainA a) { 
  2.   if (a != null) { 
  3.         assignAction; 
  4.     if (a.getB() != null) { 
  5.       otherAction; 
  6.       if (a.getB().getC() instanceof DomainC) { 
  7.         doSomethingB(); 
  8.         doSomethingA(); 
  9.         doSomthingC(); 
  10.       } 
  11.     } 
  12.   } 

上邊的嵌套判斷的代碼我相信很多人都見(jiàn)過(guò)或者寫(xiě)過(guò),這樣做雖然做到了基本的防御式編程,但是也把丑陋引了進(jìn)來(lái)。《Java 開(kāi)發(fā)手冊(cè)》中建議我們碰到這樣的情況使用衛(wèi)語(yǔ)句的方式處理。什么是衛(wèi)語(yǔ)句?我們給出個(gè)例子來(lái)說(shuō)明什么是衛(wèi)語(yǔ)句。

  1. public void doSomething(DomainA a) { 
  2.     if (a == null) { 
  3.         return ; //log some errorA 
  4.     } 
  5.     if (a.getB() == null) { 
  6.         return ; //log some errorB 
  7.     } 
  8.     if (!(a.getB().getC instanceof DomainC)) { 
  9.         return ;//log some errorC 
  10.     } 
  11.     assignAction; 
  12.     otherAction; 
  13.     doSomethingA(); 
  14.     doSomethingB(); 
  15.     doSomthingC(); 

方法中的條件邏輯使人難以看清正常的分支執(zhí)行路徑,所謂的衛(wèi)語(yǔ)句的做法就是將復(fù)雜的嵌套表達(dá)式拆分成多個(gè)表達(dá)式,我們使用衛(wèi)語(yǔ)句表現(xiàn)所有特殊情況。

使用驗(yàn)證器 (validator)

驗(yàn)證器是我在開(kāi)發(fā)中的一種實(shí)踐,將合法性檢查與 OOP 結(jié)合是一種非常奇妙的體驗(yàn)。

  1. public List<DemoResult> demo(DemoParam dParam) { 
  2.     Assert.isTrue(dParam.validate(),()-> new SysException("參數(shù)驗(yàn)證失敗-" + DemoParam.class.getSimpleName() +"驗(yàn)證失敗:" + dParam)); 
  3.     DemoResult demoResult = doBiz(); 
  4.     doSomething(); 
  5.     return demoResult; 

在這個(gè)示例中,方法的第一句話就是對(duì)驗(yàn)證器的調(diào)用,以獲得當(dāng)前參數(shù)是否合法。

在參數(shù)對(duì)象中實(shí)現(xiàn)驗(yàn)證接口,為字段配置驗(yàn)證注解,如果需要組合驗(yàn)證復(fù)寫(xiě) validate0 方法。這樣就把合法性驗(yàn)證邏輯封裝到了對(duì)象中。

  1. public class DemoParam extends BaseDO implements ValidateSubject { 
  2.     @ValidateString(strMaxLength = 128) 
  3.     private String aString; 
  4.     @ValidateObject(require = true
  5.     private List<SubjectDO> bList; 
  6.     @ValidateString(require = true,strMaxLength = 128) 
  7.     private String cString; 
  8.     @ValidateLong(require = true
  9.     private Long dLong; 
  10.     @Override 
  11.     public boolean validate0(ValidateSubject validateSubject) throws ValidateException { 
  12.         if (validateSubject instanceof DemoParam) { 
  13.             DemoParam param = (DemoParam)validateSubject; 
  14.             return StringUtils.isNotBlank(param.getAString()) 
  15.                    && SubjectDO.allValidate(param.getBList()); 
  16.         } 
  17.         return false
  18.     } 

使用斷言

當(dāng)出現(xiàn)了一個(gè)突如其來(lái)的線上問(wèn)題,我相信很多伙伴的心中一定閃現(xiàn)過(guò)這樣一個(gè)念頭。"這不科學(xué)啊...這不可能發(fā)生啊…","計(jì)數(shù)器怎么可能為負(fù)數(shù)","這個(gè)對(duì)象不可為null",但它就是真實(shí)的發(fā)生了,它就在那。我們不要這樣騙自己,特別是在編碼時(shí)。如果它不可能發(fā)生,用斷言確保它不會(huì)發(fā)生。

使用斷言的重要原則就是,斷言不能有副作用,也絕不能把必須執(zhí)行的代碼放入斷言。

斷言不能有副作用,如果我每年增加錯(cuò)誤檢查代碼卻制造了新的錯(cuò)誤,那是一件令人尷尬的事情。舉一個(gè)反面例子:

  1. while (iter.next() != null) { 
  2.     assert(iter.next()!=null); 
  3.     Object next = iter.next(); 
  4.     //... 

必須執(zhí)行的代碼也不能放入斷言,因?yàn)樯a(chǎn)環(huán)境很可能是關(guān)閉 Java 斷言的。因此我更喜歡使用 Spring 提供的 Assert 工具,這個(gè)工具提供的斷言只會(huì)返回 IllegalStateException,如果需要這個(gè)異常不能滿足我們的業(yè)務(wù)需求,我們可以重新創(chuàng)建一個(gè) Assert 類并繼承org.springframework.util.Assert,在新類中新增斷言方法以支持自定義異常的傳入。

  1. public class Assert extends org.springframework.util.Assert { 
  2.     public static <T extends RuntimeException> void isTrue(boolean expression, Supplier<T> tSupplier) { 
  3.         if (!expression) { 
  4.             if (tSupplier != null) { 
  5.                 throw tSupplier.get(); 
  6.             } 
  7.             throw new IllegalArgumentException(); 
  8.         } 
  9.     } 

  1. Assert.isTrue(crParam.validate(),()-> new SysException("參數(shù)驗(yàn)證失敗-" + Calculate.class.getSimpleName() +"驗(yàn)證失敗:" + crParam)); 

有人認(rèn)為斷言僅是一種調(diào)試工具,一旦代碼發(fā)布后就應(yīng)該關(guān)閉斷言,因?yàn)閿嘌詴?huì)增加一些開(kāi)銷(微小的 CPU 時(shí)間)。所以在很多工程實(shí)踐中斷言確實(shí)是關(guān)閉的,也有不少大 V 有過(guò)這樣的建議。Dndrew Hunt 和 David Thomas 反對(duì)這樣的說(shuō)法,在他們書(shū)里有一個(gè)比喻我認(rèn)為很形象。

在你把程序交付使用時(shí)關(guān)閉斷言就像是因?yàn)槟阍?jīng)成功過(guò),就不用保護(hù)網(wǎng)取走鋼絲。

——《The pragmatic Programmer》

處理錯(cuò)誤時(shí)的關(guān)鍵選擇

防御式編程會(huì)預(yù)設(shè)錯(cuò)誤處理。

在錯(cuò)誤發(fā)生后的后續(xù)流程上通常會(huì)有兩種選擇,終止程序和繼續(xù)運(yùn)行。

  • 終止程序,如果出現(xiàn)了非常嚴(yán)重的錯(cuò)誤,那最好終止程序或讓用戶重啟程序。比如,銀行的 ATM 機(jī)出現(xiàn)了錯(cuò)誤,那就關(guān)閉設(shè)備,以防止取 100 塊吐出 10000 塊的悲劇發(fā)生。
  • 繼續(xù)運(yùn)行,通常也是有兩種選擇,本地處理和拋出錯(cuò)誤。本地處理通常會(huì)使用默認(rèn)值的方式處理,拋出錯(cuò)誤會(huì)以異常或者錯(cuò)誤碼的形式返回。

在處理錯(cuò)誤的時(shí)候我們還面臨著另外一種選擇,正確性和健壯性的選擇。

  • 正確性,選擇正確性意味著結(jié)果永遠(yuǎn)是正確的,如果出錯(cuò),寧愿不給出結(jié)果也不要給定一個(gè)不準(zhǔn)確的值。比如用戶資產(chǎn)類的業(yè)務(wù)。
  • 健壯性,健壯性意味著通過(guò)一些措施,保證軟件能夠正常運(yùn)行下去,即使有時(shí)候會(huì)有一些不準(zhǔn)確的值出現(xiàn)。比如產(chǎn)品介紹超過(guò)頁(yè)面展示范圍

無(wú)論是使用衛(wèi)語(yǔ)、斷言還是預(yù)設(shè)錯(cuò)誤處理都是在用抱著對(duì)程序世界的敬畏態(tài)度在小心的駕駛,時(shí)刻提防著他人更提防著自己。

北京第三區(qū)交通委提醒您,道路千萬(wàn)條,安全第一條,行車不規(guī)范,親人兩行淚。

正確使用異常

檢查每一個(gè)可能的錯(cuò)誤是一種良好的實(shí)踐,特別是那些意料之外的錯(cuò)誤。

非常棒的是,Java 為我們提供了異常機(jī)制。如果充分發(fā)揮異常的優(yōu)點(diǎn),可以提高程序的可讀性、可靠性和可維護(hù)性,但如果使用不當(dāng),異常帶來(lái)的負(fù)面影響也是非常值得我們注意并避免的。

只在異常情況下使用異常

在《The pragmatic Programmer》和《Effective Java》中作者都有這樣的觀點(diǎn)。

我認(rèn)為這有兩重意思。一重意思如何處理識(shí)別異常情況并處理他,另一重意思是只在異常情況下使用異常流程。

那什么是異常情況,又該如何處理?這個(gè)問(wèn)題無(wú)法在代碼模式上給出標(biāo)準(zhǔn)的答案,完全看實(shí)際情況,要對(duì)每一個(gè)錯(cuò)誤了然于胸并檢查每一個(gè)可能發(fā)生的錯(cuò)誤,并區(qū)分錯(cuò)誤和異常。

即便同樣是打開(kāi)文件操作,讀取"/etc/passwd"和讀取一個(gè)用戶上傳的文件,同樣是 FileNotFoundException,如何處理完全取決于實(shí)際情況,Surprise!前者直接讀取文件出現(xiàn)異常直接拋出讓程序盡早崩潰,而后者應(yīng)該先判斷文件是否存在,如果存在但出現(xiàn)了 FileNotFoundException 則再拋出。

  1. public static void openPasswd() throws FileNotFoundException { 
  2.         FileInputStream fs = new FileInputStream("/etc/passwd"); 
  3.     } 

讀取"/etc/passwd"失敗,Surprise!

  1. public static boolean openUserFile(String path) throws FileNotFoundException { 
  2.        File f = new File(path); 
  3.        if (!f.exists()) { 
  4.            return false
  5.        } 
  6.        FileInputStream fs = new FileInputStream(path); 
  7.        return true
  8.    } 

在文件存在的情況下讀取文件失敗,Surprise!

再啰嗦一遍,是不是異常情況關(guān)鍵在于它是不是給我們一記 Surprise!,這就是本節(jié)開(kāi)頭檢查每一個(gè)錯(cuò)誤是一種良好的實(shí)踐想要表達(dá)的。

使用異常來(lái)控制正常流程的反面例子我就偷懶借用一下《Effective Java Second Edition》里的例子來(lái)說(shuō)明好了。

  1. Integer[] range ={1,2,3}; 
  2. //Horrible abuse of exceptions.Don't ever do this! 
  3. try { 
  4.   int i=0; 
  5.   println(range[i++].intValue()); 
  6. } catch (ArrayIndexOutOfBoundsException e) {} 

這個(gè)例子看起來(lái)根本不知道在干什,這段代碼其實(shí)是在用數(shù)組越界異常來(lái)控制遍歷數(shù)組,這個(gè)腦洞開(kāi)的非常拙劣。如何正確遍歷一個(gè)數(shù)組我想不需要再給出例子,那是對(duì)讀者的褻瀆。

那為什么有人這么開(kāi)腦洞呢?因?yàn)檫@個(gè)做法企圖使用 Java 錯(cuò)誤判斷機(jī)制來(lái)提高性能,因?yàn)?JVM 對(duì)每一次數(shù)組訪問(wèn)都會(huì)檢查越界情況,所以他們認(rèn)為檢查到的錯(cuò)誤才應(yīng)該是循環(huán)終止的條件,然而 for-each 循環(huán)對(duì)已經(jīng)檢查到的錯(cuò)誤視而不見(jiàn),將其隱藏了,所以用應(yīng)該避免使用 for-each。

對(duì)于這個(gè)腦洞的原因 Joshua Bloch 給出了三點(diǎn)反駁:

  • 因?yàn)楫惓C(jī)制的設(shè)計(jì)初衷是用于不正常的情形,所以很少會(huì)有 JVM 實(shí)現(xiàn)試圖對(duì)它們進(jìn)行優(yōu)化,使得與顯示測(cè)試一樣快速。
  • 把代碼放在 try-catch 塊中反而阻止了現(xiàn)代 JVM 實(shí)現(xiàn)本來(lái)可能要執(zhí)行的某些特定優(yōu)化。
  • 對(duì)數(shù)組進(jìn)行遍歷的標(biāo)準(zhǔn)模式并不會(huì)導(dǎo)致冗余的檢查。有些現(xiàn)代的 JVM 實(shí)現(xiàn)會(huì)將他們優(yōu)化掉。

還有一個(gè)例子是我曾經(jīng)遇到的,但是由于年代久遠(yuǎn)已經(jīng)找不到項(xiàng)目地址了。我一個(gè)朋友曾經(jīng)給我看過(guò)一個(gè) github 上的 MVC 框架項(xiàng)目,雖然時(shí)隔多年但令我印象深刻的是這個(gè)項(xiàng)目使用自定義異常和異常碼來(lái)控制 Dispatcher,把異常當(dāng)成一種方便的結(jié)果傳遞方式來(lái)使用,當(dāng)成 goto 來(lái)使用,這太可怕了。不過(guò) try-catch 方式從字節(jié)碼表述上來(lái)看,確實(shí)是一種 goto 的表述。這樣的方式我們最好想都不要想。

這兩個(gè)例子主要就是為了說(shuō)明,異常應(yīng)該只用于異常的情況下;永遠(yuǎn)不應(yīng)該用在正常的流程中,不管你的理由看著多么的聰明。這樣做往往會(huì)弄巧成拙,使得代碼可讀性大大下降。

受檢異常和非受檢異常

曾經(jīng)不止一次的見(jiàn)過(guò)有人提倡將系統(tǒng)中的受檢異常都包裝成非受檢異常,對(duì)于這個(gè)建議我并不以為然。因?yàn)?Java 的設(shè)計(jì)者其實(shí)是希望通過(guò)區(qū)分異常種類來(lái)指導(dǎo)我們編程的。

Java 一共提供了三類可拋出結(jié)構(gòu) (throwable),受檢異常、非受檢異常(運(yùn)行時(shí)異常)和錯(cuò)誤 (error)。他們的界限我也經(jīng)常傻傻的分不清,不過(guò)還是有跡可循的。

  • 受檢異常:如果期望調(diào)用者能夠適當(dāng)?shù)幕謴?fù),比如 RMI 每次調(diào)用必須處理的異常,設(shè)計(jì)者是期望調(diào)用者可以重試或別的方式來(lái)嘗試恢復(fù);比如上邊提到的 FileInputStream 的構(gòu)造方法,會(huì)拋出 FileNotFoundException,設(shè)計(jì)者或許希望調(diào)用者嘗試從其他目錄來(lái)讀取該文件,使得程序可以繼續(xù)執(zhí)行下去。
  • 非受檢異常和錯(cuò)誤:表明是編程錯(cuò)誤,往往屬于不可恢復(fù)的情景,而且是不應(yīng)該被提前捕獲的,應(yīng)該快速拋出到頂層處理器,比如在服務(wù)接口的基類方法中統(tǒng)一處理非受檢異常。這種非受檢異常往往也說(shuō)明了在編程中違反了某些約定。比如數(shù)組越界異常,說(shuō)明違反了訪問(wèn)數(shù)組不能越界的前提約定。

總而言之,對(duì)于可恢復(fù)的情況使用受檢異常;對(duì)于程序錯(cuò)誤使用非受檢異常。因此你自己程序內(nèi)部定義的異常都應(yīng)該是非受檢異常;在面向接口或面向二方/三方庫(kù)的方法盡量使用受檢異常。

說(shuō)到面向接口或面向二/三方庫(kù),你可能碰到的就是一輛失控的汽車。搞清楚你所調(diào)用的接口或者庫(kù)里的異常情況也是我們能夠碼出健壯代碼的一個(gè)強(qiáng)力保證。

不要忽略異常

這個(gè)建議顯而易見(jiàn),但卻常常被違反。當(dāng)一個(gè) API 的設(shè)計(jì)者聲明一個(gè)方法將拋出異常的時(shí)候,通常都是想要說(shuō)明某件事發(fā)生了。忽略異常就是我們通常說(shuō)的吃掉異常,try-catch 但什么也不做。吃掉一個(gè)異常就好比破壞了一個(gè)報(bào)警器,當(dāng)災(zāi)難真正來(lái)臨沒(méi)人搞清楚發(fā)生了什么。

對(duì)于每一個(gè) catch 塊至少打印一條日志,說(shuō)明異常情況或者說(shuō)明為什么不處理。

這個(gè)顯而易見(jiàn)的建議同時(shí)適用于受檢異常和非受檢異常。

DRY (Don't Repeat Yourself)

DRY 原則最先在《The pragmatic Programmer》被提出,如今已經(jīng)被業(yè)界廣泛的認(rèn)知,我相信每個(gè)軟件工程師都認(rèn)識(shí)它。我想有很多人對(duì)它的認(rèn)識(shí)含混不清僅僅是不要有重復(fù)的代碼;也有些人對(duì)此原則不屑一顧抽象什么的都是浪費(fèi)時(shí)間快速上線是大義;也有人誓死捍衛(wèi)這個(gè)原則不能忍受任何重復(fù)。今天我們來(lái)談?wù)勥@個(gè)熟悉又陌生的話題。

DRY 是什么

DRY 的原則是“系統(tǒng)中的每一部分,都必須有一個(gè)單一的、明確的、權(quán)威的代表”,指的是(由人編寫(xiě)而非機(jī)器生成的)代碼和測(cè)試所構(gòu)成的系統(tǒng),必須能夠表達(dá)所應(yīng)表達(dá)的內(nèi)容,但是不能含有任何重復(fù)代碼。當(dāng) DRY 原則被成功應(yīng)用時(shí),一個(gè)系統(tǒng)中任何單個(gè)元素的修改都不需要與其邏輯無(wú)關(guān)的其他元素發(fā)生改變。此外,與之邏輯上相關(guān)的其他元素的變化均為可預(yù)見(jiàn)的、均勻的,并如此保持同步。

這段定義來(lái)自于中文維基百科,但這個(gè)定義似乎與 Andrew Hunt 和 David Thomas 給出的定義有所出入。尋根溯源在《The pragmatic Programmer》作者是這樣定義這個(gè)原則的:

EVERY PIECE OF KNOWLEDGE MUST HAVE A SINGLE, UNAMBIGUOUS, AUTHORITATIVE REPRESENTATION WITHIN A SYSTEM.

系統(tǒng)中的每一項(xiàng)知識(shí)都必須具有單一、無(wú)歧義、權(quán)威的表示。

作者所提倡禁止的是知識(shí) (knowledge) 的重復(fù)而不是單純的代碼上的重復(fù)。那什么是知識(shí)呢?我斗膽給一個(gè)自己的理解,知識(shí)就是系統(tǒng)中對(duì)于一個(gè)邏輯的解釋/定義,系統(tǒng)中的邏輯都是要對(duì)外輸出或者讓外界感知的。邏輯的定義/解釋包括代碼和寫(xiě)在代碼上的文檔還有宏觀上實(shí)現(xiàn)。我們要避免的是在改動(dòng)時(shí)的一個(gè)邏輯的時(shí)候需要去修改十處,如果漏掉了任何一處就會(huì)造成 bug 甚至線上故障。變更在軟件開(kāi)發(fā)中又是一個(gè)常態(tài),在互聯(lián)網(wǎng)行業(yè)中更是如此,而在一個(gè)到處是重復(fù)的系統(tǒng)中維護(hù)變更是非常艱難的。

沒(méi)有文檔比錯(cuò)誤的文檔更好

編寫(xiě)代碼時(shí)同時(shí)編寫(xiě)文檔在多數(shù)程序員看來(lái)是一個(gè)好習(xí)慣,但有相當(dāng)一部分程序開(kāi)發(fā)人員又沒(méi)有這樣的習(xí)慣,這一點(diǎn)反而使得代碼更干 (dry)——有點(diǎn)好笑。因?yàn)榈讓又R(shí)應(yīng)該放在代碼中,底層代碼應(yīng)該是職責(zé)單一、邏輯簡(jiǎn)單的代碼,在底層代碼上添加注釋就是在做重復(fù)的事情,就有可能因?yàn)閷?duì)于知識(shí)的過(guò)期解釋,而讀注釋比讀代碼更容易,可怕的事情往往就這樣發(fā)生;把注釋放在更上層的復(fù)雜的復(fù)雜邏輯中。滿篇的注釋并不是好代碼,也不是好習(xí)慣,好的代碼是不需要注釋的。

CP 大法,禁止!

每個(gè)項(xiàng)目都有時(shí)間壓力,這往往是誘惑我們使用 CP 大法最重要原因。但是"欲速則不達(dá)",你也許現(xiàn)在省了十分鐘,以后卻需要花幾個(gè)小時(shí)處理各種各樣的線上問(wèn)題。因?yàn)樽兏浅B(tài),我們當(dāng)初留下的一個(gè)坑隊(duì)友可能會(huì)幫你挖的更深更大一些,然后我們就掉進(jìn)了自己挖的坑,我們還會(huì)埋怨豬隊(duì)友,到底誰(shuí)才是豬隊(duì)友。這其實(shí)是我?guī)н^(guò)的一個(gè)團(tuán)隊(duì)里真實(shí)發(fā)生的事情。

把知識(shí)的解釋/定義放在一處!

PS:感受一下程序員的冷幽默。違背 DRY 原則的代碼,程序員稱之為 WET 的,可以理解為 Write Everything Twice(任何東西寫(xiě)兩遍),We Enjoying Typing(我們享受敲鍵盤(pán))或 Waste Everyone’s Time(浪費(fèi)所有人的時(shí)間)。

關(guān)于 DRY 原則的爭(zhēng)論

DRY 原則提出以來(lái)一直以來(lái)都存在著一些爭(zhēng)議和討論,有粉也有黑。如果有一個(gè)百分比,對(duì)于這條原則我會(huì)選擇 95% 服從。

《The pragmatic Programmer》告訴我們 Once and only once。

《Extreme Programing》又告訴我們 You aren't gonna need it (YAGNI),指的是你自以為有用的功能,實(shí)際上都是用不到的。這里好像出現(xiàn)了一個(gè)問(wèn)題,DRY 與 YAGNI 不完全兼容。DRY 要求花精力去抽象追求通用,而 YAGNI 要求快和省,你花精力做的抽象很可能用不到。

這個(gè)時(shí)候我們的第三選擇是什么?《Refactoring》提出的 Rule Of Three 像是一個(gè)很好的折中方案。它的涵義是,第一次用到某個(gè)功能時(shí),你寫(xiě)一個(gè)特定的解決方法;第二次又用到的時(shí)候,你拷貝上一次的代碼;第三次出現(xiàn)的時(shí)候,你才著手"抽象化",寫(xiě)出通用的解決方法。這樣做有幾個(gè)理由:

省事

如果一種功能只有一到兩個(gè)地方會(huì)用到,就不需要在"抽象化"上面耗費(fèi)時(shí)間了。

容易發(fā)現(xiàn)模式

"抽象化"需要找到問(wèn)題的模式,問(wèn)題出現(xiàn)的場(chǎng)合越多,就越容易看出模式,從而可以更準(zhǔn)確地"抽象化"。比如,對(duì)于一個(gè)數(shù)列來(lái)說(shuō),兩個(gè)元素不足以判斷出規(guī)律:

  1. 1,2,_,_,_,_ 

第三個(gè)元素出現(xiàn)后,規(guī)律就變得較清晰了:

  1. 1,2,4,_,_,_ 

防止過(guò)度冗余

如果一種功能同時(shí)有多個(gè)實(shí)現(xiàn),管理起來(lái)非常麻煩,修改的時(shí)候需要修改多處。在實(shí)際工作中,重復(fù)實(shí)現(xiàn)最多可以容忍出現(xiàn)一次,再多就無(wú)法接受了。

我認(rèn)為以上三個(gè)原則都不能當(dāng)做銀彈,還是要根據(jù)實(shí)際情況做出正確的選擇。

DRY 原則理論上來(lái)說(shuō)是沒(méi)有問(wèn)題的,但在實(shí)際應(yīng)用時(shí)切忌死搬教條。它只能起指導(dǎo)作用,沒(méi)有量化標(biāo)準(zhǔn),否則的話理論上一個(gè)程序每一行代碼都只能出現(xiàn)一次才行,這是非常荒謬的。

Rule of Three 不是重復(fù)的代碼一定要出現(xiàn)三次才可以進(jìn)行抽象,我認(rèn)為三次不應(yīng)該成為一個(gè)度量標(biāo)準(zhǔn),對(duì)于未來(lái)的預(yù)判和對(duì)于項(xiàng)目走向等因素也應(yīng)該放在是否抽象的考慮中。

PS:王垠曾經(jīng)寫(xiě)過(guò)一篇《DRY 原則的危害》有興趣的朋友可以讀一讀:如何評(píng)價(jià)王垠最新文章,《DRY 原則的危害》?

(https://www.zhihu.com/question/31278077)

后記

原則不是銀彈,原則是沙漠中的綠洲亦或是沙漠中海市蜃樓中的綠洲。面對(duì)所謂的原則要求我們每一個(gè)人都有辨識(shí)能力,不盲目遵從先哲大牛,要具有獨(dú)立思考的能力。具備辨識(shí)和思考能力首先就需要有足夠多的輸入和足夠多的實(shí)踐。

參考

[1]《The pragmatic Programmer:From Journeyman to Master》 作者:Andrew Hunt、David Thomas[2]《Effective Java Second Edition》 作者 :Joshua Bloch[3]《Java 開(kāi)發(fā)手冊(cè)》[4]中文維基百科[5]代碼的抽象三原則-阮一峰

 

http://www.ruanyifeng.com/blog/2013/01/abstraction_principles.html

 

責(zé)任編輯:武曉燕 來(lái)源: 51CTO專欄
相關(guān)推薦

2020-05-11 15:23:58

CQRS代碼命令

2021-09-01 08:55:20

JavaScript代碼開(kāi)發(fā)

2013-06-07 14:00:23

代碼維護(hù)

2021-11-30 10:20:24

JavaScript代碼前端

2021-01-04 07:57:07

C++工具代碼

2022-02-17 10:05:21

CSS代碼前端

2022-02-08 19:33:13

技巧代碼格式

2019-09-20 15:47:24

代碼JavaScript副作用

2020-05-19 15:00:26

Bug代碼語(yǔ)言

2020-12-19 10:45:08

Python代碼開(kāi)發(fā)

2022-03-11 12:14:43

CSS代碼前端

2022-10-24 08:10:21

SQL代碼業(yè)務(wù)

2015-09-28 10:49:59

代碼程序員

2019-06-24 10:26:15

代碼程序注釋

2015-05-11 10:48:28

代碼干凈的代碼越少越干凈

2022-06-16 14:07:26

Java代碼代碼review

2020-05-14 09:15:52

設(shè)計(jì)模式SOLID 原則JS

2021-07-19 08:24:36

阿里代碼程序員

2016-11-25 13:50:15

React組件SFC

2017-03-15 13:41:16

數(shù)據(jù)庫(kù)SQL調(diào)試
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

欧美韩日高清| 两根大肉大捧一进一出好爽视频| 亚洲久久久久久| 亚洲黄色中文字幕| 国产精品美女久久久久久久| 97人人香蕉| 国产成人无码av| 2023国产精品久久久精品双| 日韩av在线免播放器| 视频在线观看免费高清| 国产精选在线| 日韩一区日韩二区| 九九99久久| jlzzjlzz亚洲女人18| 亚洲欧美网站| 欧美丰满少妇xxxxx做受| 亚洲自拍偷拍图| 国产乱论精品| 91精品国产综合久久精品性色| 啊啊啊一区二区| 中文av资源在线| 日本一区二区免费在线| 精品日本一区二区三区| 国产av精国产传媒| 日本在线不卡视频一二三区| 午夜精品一区二区三区在线 | 狼人精品一区二区三区在线 | 亚洲乱码精品久久久久..| 久久一二三区| 国产91精品久久久久久| 亚洲av鲁丝一区二区三区| 欧美亚洲激情| 亚洲剧情一区二区| 国产精品手机在线观看| 激情综合五月| 91精品国产综合久久久久久久久久| 午夜精品久久久内射近拍高清| 丁香花视频在线观看| 亚洲三级电影网站| 在线视频精品一区| 亚洲xxxxxx| 国产精品女同一区二区三区| 亚洲欧美日韩中文字幕一区二区三区 | 国产精品久久久久久久久快鸭 | 韩国中文字幕hd久久精品| 蓝色福利精品导航| 国产精品视频精品| 国产在线观看第一页| 香蕉亚洲视频| 欧美在线视频一区二区| 黄色一级片免费看| 99在线观看免费视频精品观看| 国产综合在线看| 国产污视频在线看| 一区久久精品| 97婷婷大伊香蕉精品视频| 国产一级做a爰片在线看免费| 欧美激情综合| 久久久久久久久爱| 日本在线视频免费观看| 亚洲精品看片| 人人澡人人澡人人看欧美| 久久久久久少妇| 久久综合婷婷| 91精品国产综合久久香蕉的用户体验| 91禁在线观看| 国产成人午夜电影网| 成人h视频在线观看| 午夜在线观看视频18| 久久综合九色综合欧美98| 色噜噜狠狠色综合网| 三区四区电影在线观看| 亚洲色图欧洲色图| 男人天堂手机在线视频| 国模精品视频| 欧美优质美女网站| 亚洲综合20p| 精品久久97| 国产亚洲aⅴaaaaaa毛片| 在线观看天堂av| 欧美搞黄网站| 日本aⅴ大伊香蕉精品视频| 青青艹在线观看| 国产成人精品午夜视频免费 | 熟妇高潮一区二区三区| 精品一区二区三区在线观看视频| 亚洲国产91| 久久人人看视频| 黄色在线免费观看| 久久电影网站中文字幕| 成人动漫视频在线观看免费| 你懂的在线观看| 国产精品国产三级国产专播品爱网 | 国产精品日韩在线| www天堂在线| 久久伊人中文字幕| 中文字幕久久一区| 成年人在线网站| 欧美日韩aaa| 少妇被狂c下部羞羞漫画| 欧美一区2区| 欧美极品少妇xxxxⅹ免费视频 | 激情综合网av| 国产伦理久久久| 高清在线观看av| 亚洲一区二区在线播放相泽| www黄色在线| 国产伦精品一区二区三区在线播放| 伊人精品在线观看| 国产亚洲精品久久777777| 日韩精品一级中文字幕精品视频免费观看 | 久久伊人色综合| 精品不卡一区二区| 粉嫩av一区二区三区粉嫩| 日韩av在线一区二区三区| 91av久久| 日韩一区二区精品在线观看| 色综合99久久久无码国产精品| 激情文学一区| 91老司机在线| 在线观看完整版免费| 精品久久香蕉国产线看观看gif| 中文字幕在线视频一区二区三区 | 国产乱码精品一区二区三区av| 成人精品免费看| 成人综合色站| 黄色av电影在线观看| 在线免费不卡视频| 中文字幕xxx| 亚洲精品护士| 国产伦精品一区二区三区在线 | 女人18毛片水真多18精品| 中文字幕在线免费不卡| www.欧美日本| 国产精品日韩精品中文字幕| 国内精品久久久| 精品人妻少妇AV无码专区| 成人欧美一区二区三区白人| 手机看片福利日韩| 九九在线精品| 日本aⅴ大伊香蕉精品视频| 香蕉久久国产av一区二区| 亚洲综合另类小说| 女同性αv亚洲女同志| 亚洲澳门在线| 亚洲影影院av| av黄在线观看| 日韩欧美在线123| 91日韩中文字幕| 国产美女精品在线| 久久av喷吹av高潮av| www.91精品| 不卡av电影院| 精品女同一区二区三区| 亚洲成在人线在线播放| 国模无码视频一区| 亚洲精品在线二区| 久久久免费看| 性欧美1819sex性高清| 一区二区欧美激情| 96日本xxxxxⅹxxx17| 亚洲欧美日韩在线播放| 制服.丝袜.亚洲.中文.综合懂| 亚洲黄网站黄| 欧美精品久久久| 91大神在线观看线路一区| 一区二区三区四区精品| 国产精品欧美综合亚洲| 亚洲一区在线电影| av网站免费在线播放| 久久久噜噜噜久久狠狠50岁| 亚洲国内在线| 免费精品一区二区三区在线观看| 欧美精品18videosex性欧美| 视频国产在线观看| 欧美久久久久久久久久| 黄色一级片在线| 91视频在线观看免费| 污色网站在线观看| 国模 一区 二区 三区| 蜜桃999成人看片在线观看| 日本一区二区电影| 久久天天躁日日躁| 色偷偷在线观看| 欧亚洲嫩模精品一区三区| 国产美女福利视频| 91麻豆精品视频| 污污网站在线观看视频| 影音先锋久久久| 亚洲精品9999| 盗摄系列偷拍视频精品tp| 国产福利精品视频| 成人日日夜夜| 亚洲男人天堂视频| 国产成人免费看一级大黄| 日韩欧美一区二区三区| 91视频综合网| 日本一区二区三区高清不卡| 在线观看你懂的视频| 视频一区视频二区中文| 337p亚洲精品色噜噜狠狠p| 国产精品亚洲片在线播放| 亚洲综合av影视| 欧美三级网址| 国内揄拍国内精品| 欧美成年黄网站色视频| 日韩极品精品视频免费观看| 97精品人妻一区二区三区| 精品电影在线观看| 日本少妇高清视频| 欧美激情在线看| 30一40一50老女人毛片| 国产精品中文欧美| 久久九九国产视频| 亚洲激情不卡| 免费的av在线| 日韩综合网站| 日本精品一区二区三区视频| 精品国产乱子伦一区二区| 成人网址在线观看| 超碰这里只有精品| 欧美最顶级丰满的aⅴ艳星| 九色91在线| 久久伊人91精品综合网站| av电影在线观看一区二区三区| 亚洲精品国产精品国自产在线| 性欧美18一19性猛交| 欧美久久一区二区| 一级aaaa毛片| 欧美午夜电影一区| 久久亚洲精品石原莉奈| 欧美日韩午夜剧场| 中文字幕一区二区三区手机版| 一区二区免费看| 日韩欧美中文字幕视频| 亚洲欧美日韩国产综合| 99自拍视频在线| 亚洲色图19p| 国产一区二区播放| 亚洲精品高清在线| 一区二区视频免费看| 亚洲人成网站精品片在线观看| 久久一级免费视频| 成人免费小视频| 天堂网avav| 亚洲精选一二三| 综合五月激情网| 亚洲欧美电影院| 亚洲色婷婷一区二区三区| 亚洲桃色在线一区| 久久久久亚洲av片无码| 亚洲视频综合在线| 欧产日产国产v| 亚洲高清久久久| 日韩免费观看一区二区| 欧美视频第一页| 天堂网视频在线| 欧美性色黄大片| 91精品人妻一区二区三区果冻| 制服丝袜国产精品| www精品国产| 日韩成人在线电影网| 男女污视频在线观看| 尤物yw午夜国产精品视频| 日本最新在线视频| 免费av一区二区| 草美女在线观看| 欧美在线视频一区二区| 国模一区二区| 91中文字幕在线观看| 国产精品tv| 欧美日韩成人一区二区三区| 成人在线视频免费观看| 国产成人一二三区| 国产精品综合| 九九九九九国产| 成人午夜电影小说| 久久久久久久毛片| 一区二区三区免费观看| 青草视频在线观看免费| 欧美精品色一区二区三区| 高h放荡受浪受bl| 亚洲无亚洲人成网站77777| 国产美女在线观看| 97avcom| 欧美爱爱视频| 成人国产1314www色视频| 国产99精品| 免费看日b视频| 视频一区视频二区中文字幕| 国产精品19p| 国产日韩欧美电影| 久久久久久天堂| 欧美在线免费观看视频| 成人av无码一区二区三区| 亚洲天堂av在线播放| 香蕉久久aⅴ一区二区三区| 日韩免费av片在线观看| 草草视频在线一区二区| 亚洲国产高清国产精品| 亚洲欧洲综合| 99999精品| 国产亚洲一区二区三区在线观看| 久久成人国产精品入口| 欧美午夜宅男影院| 天堂中文在线资源| 久久成人亚洲精品| 99久久er| 欧美在线播放一区| 精品999成人| 国产91在线免费观看| 中文字幕的久久| 久久久久久久久久免费视频 | 亚洲三级在线观看视频| 91网站在线播放| 久热这里只有精品在线| 67194成人在线观看| 成人亚洲综合天堂| 欧美一级高清免费播放| 超碰成人免费| 久久香蕉视频网站| 激情av综合网| 18精品爽国产三级网站| 色婷婷精品久久二区二区蜜臀av| 日本免费一区视频| 欧美黑人性猛交| 欧美日韩黄色| 自拍偷拍亚洲色图欧美| 日本亚洲免费观看| 亚洲国产av一区| 欧美视频在线观看免费| 无码国产色欲xxxx视频| 久久久久久国产精品三级玉女聊斋| 91精品视频一区二区| 亚洲免费av网| 久久成人久久爱| 日韩一区二区三区四区视频| 欧美日韩精品欧美日韩精品一 | 久久精品最新地址| 久久爱.com| 一区二区三视频| 久久福利视频一区二区| 亚洲欧美精品aaaaaa片| 欧美久久久一区| 超碰超碰在线| av在线不卡观看| 国产精品sm| 人妻av一区二区| 精品高清美女精品国产区| 少妇喷水在线观看| 91国内产香蕉| 亚洲精品国产动漫| 日本新janpanese乱熟| 亚洲国产电影在线观看| 亚洲系列第一页| 久久亚洲欧美日韩精品专区| 秋霞午夜一区二区三区视频| 日韩欧美猛交xxxxx无码| 99视频一区二区三区| 国产免费av一区| 色噜噜久久综合伊人一本| 伊人国产精品| 欧美久久在线观看| 91论坛在线播放| 制服丝袜在线一区| 欧美成人激情在线| 精品久久97| 黄色成人免费看| 亚洲美女偷拍久久| 四虎影视2018在线播放alocalhost| 热re99久久精品国产66热| 久久美女视频| 少妇欧美激情一区二区三区| 午夜精品福利一区二区蜜股av | 久久网站热最新地址| 久久这里只有精品9| 久久国产精品久久久| 久久影院资源站| 黄色在线视频网| 一区2区3区在线看| 四虎影视精品成人| 91精品在线观看视频| 一区二区三区精品视频在线观看| 老熟妇一区二区| 欧美成人精精品一区二区频| 视频二区不卡| 国产精品久久成人免费观看| av一本久道久久综合久久鬼色| 中文字幕在线观看你懂的| 欧美大肥婆大肥bbbbb| 亚洲自拍都市欧美小说| 一区二区三区人妻| 欧美丝袜美女中出在线| 国产在线更新| 欧美高清视频一区| 国产成人午夜精品5599| 亚洲综合网av| 777777777亚洲妇女| 亚洲影视一区| 午夜啪啪免费视频|