解读阿里官方代码规范
2017年開(kāi)春,阿里對(duì)外公布了「阿里巴巴Java開(kāi)發(fā)手冊(cè)」從頭到尾瀏覽了一遍這份手冊(cè)之后,感覺(jué)很棒。雖然其中的某些觀點(diǎn)筆者不能茍同,但大部分的規(guī)范還是值得絕大多數(shù)程序員學(xué)習(xí)和遵守的。
筆者將對(duì)這份代碼規(guī)范中的一些細(xì)節(jié)做一些解讀,包含筆者的觀點(diǎn)和想法,可以作為這份代碼規(guī)范的擴(kuò)展閱讀。對(duì)于規(guī)范中某些「顯而易見(jiàn)」的條款,將不在解讀范圍之列(換言之,這都不懂,就說(shuō)明你天賦不夠,乘早別做程序員了)。
當(dāng)然,筆者在日常的編程過(guò)程中屬于「代碼潔癖偏執(zhí)狂」,所以文中的某些觀點(diǎn)僅代表個(gè)人看法,請(qǐng)勿人生攻擊。
阿里官方代碼規(guī)范解讀系列總計(jì)五篇,已在本公眾號(hào)發(fā)過(guò),本篇為合集,對(duì)之前文章中的部分內(nèi)容作了修訂。
命名規(guī)約
1.1.1 代碼中的命名均不能以下劃線或美元符號(hào)開(kāi)始,也不能以下劃線或美元符號(hào)結(jié)束1.1.2 代碼中的命名嚴(yán)禁使用拼音與英文混合的方式,更不允許直接使用中文的方式1.1.3 / 1.1.4 類(lèi)名使用UpperCamelCase風(fēng)格,必須遵從駝峰形式(某些情況諸如領(lǐng)域模型相關(guān)的命名除外);方法名、參數(shù)名、成員變量、局部變量都統(tǒng)一使用lowerCamelCase風(fēng)格,必須遵從駝峰形式1.1.5 常量命名全部大寫(xiě),單詞間用下劃線隔開(kāi)1.1.9 包名統(tǒng)一使用小寫(xiě),點(diǎn)分隔符之間有且僅有一個(gè)自然語(yǔ)義的英語(yǔ)單詞上述規(guī)則,主要是規(guī)定了你書(shū)寫(xiě)Java的時(shí)候,哪些字符可以用,什么時(shí)候用大寫(xiě),什么時(shí)候用小寫(xiě)。應(yīng)該說(shuō),絕大多數(shù)寫(xiě)Java的都遵循著上述的規(guī)范,就像筆者說(shuō)的:尼瑪這都不懂,乘早改行別寫(xiě)Java了。
筆者在實(shí)際編程過(guò)程中,對(duì)類(lèi)名的風(fēng)格,可能更激進(jìn)一些,根據(jù)阿里的規(guī)范:
類(lèi)名使用UpperCamelCase風(fēng)格,必須遵從駝峰形式,但以下情形例外:(領(lǐng)域模型的相關(guān)命名)DO / BO / DTO / VO等實(shí)際上還是有可能會(huì)存在著諸如:UserVO,UserDTO,UserDAO這樣的命名。對(duì)不起,在筆者的團(tuán)隊(duì)中,這樣的命名也會(huì)被禁止,這里分為2種情況:
禁止使用 VO / BO / DTO / VO 等進(jìn)行領(lǐng)域模型的命名有讀者要問(wèn),那么如果萬(wàn)一項(xiàng)目中要使用DTO或者VO咋辦?筆者的觀點(diǎn)如下:
第一,項(xiàng)目中避免使用DTO或者VO,DTO是一個(gè)早在2004年就被討論并認(rèn)定為一個(gè)反模式的東西;
第二,誰(shuí)規(guī)定領(lǐng)域模型就一定要用DTO或者VO做結(jié)尾?還原領(lǐng)域模型的本來(lái)意義才是命名的核心,一個(gè)User在實(shí)際業(yè)務(wù)系統(tǒng)中可能是一個(gè)Admin或者Supervisor,那就直接用Admin來(lái)命名,而不是把User轉(zhuǎn)化成UserVO,UserVO啥都不是,是初級(jí)程序員造出來(lái)的一個(gè)怪胎。
所有的DAO使用正常的駝峰法進(jìn)行命名,例如:UserDao對(duì)上面這條,或許有很多DAO大寫(xiě)黨要發(fā)飆了。其實(shí)筆者就想反問(wèn)這些人一句:你咋不把UserService寫(xiě)成UserSERVICE呢?
命名原則
1.1.5 力求語(yǔ)義表達(dá)完整清楚,不要嫌名字長(zhǎng)1.1.10 杜絕完全不規(guī)范的縮寫(xiě),避免望文不知義這兩條說(shuō)的是命名的基本原則,總的來(lái)說(shuō)其實(shí)表達(dá)了一個(gè)意思:你他媽的別給我用縮寫(xiě)!
其實(shí)有很多程序員會(huì)非常神奇的患上「縮寫(xiě)綜合癥」。比如非常典型的就是:UserMgmt,這他媽是什么鬼?多敲幾個(gè)字母會(huì)死么?
類(lèi)的命名
1.1.6 抽象類(lèi)命名使用Abstract或Base開(kāi)頭;異常類(lèi)命名使用Exception結(jié)尾;測(cè)試類(lèi)命名以它要測(cè)試的類(lèi)的名稱(chēng)開(kāi)始,以Test結(jié)尾1.1.13 對(duì)于Service和DAO類(lèi),基于SOA的理念,暴露出來(lái)的服務(wù)一定是接口,內(nèi)部的實(shí)現(xiàn)類(lèi)用Impl的后綴與接口區(qū)別1.1.13 如果是形容能力的接口名稱(chēng),取對(duì)應(yīng)的形容詞做接口名1.1.14 枚舉類(lèi)名建議帶上Enum后綴,枚舉成員名稱(chēng)需要全大寫(xiě),單詞間用下劃線隔開(kāi)1.1.11 如果使用到了設(shè)計(jì)模式,建議在類(lèi)名中體現(xiàn)出具體模式1.1.9 包名統(tǒng)一使用單數(shù)形式;類(lèi)名如果有復(fù)數(shù)含義,類(lèi)名可以使用復(fù)數(shù)形式上述規(guī)則,主要在講述具體命名一個(gè)類(lèi)的時(shí)候的一個(gè)「用詞規(guī)范」。這些用詞規(guī)范絕大多數(shù)實(shí)際上也是一種約定俗成,比如Abstract前綴,Exception后綴等等。
對(duì)于接口的命名,筆者最為不能忍受的一種命名,就是將所有的接口以大寫(xiě)字母I開(kāi)頭,諸如:IUserService。真是一種坑爹到極致的命名:第一,IUserService糾結(jié)是一個(gè)啥玩意兒?好好的UserService,加上一個(gè)大寫(xiě)字母I,直接失去了閱讀時(shí)的語(yǔ)義性;第二,誰(shuí)他媽知道你這東西到底是大寫(xiě)字母I還是數(shù)字1啊?
有關(guān)枚舉類(lèi)名是否加上Enum后綴,筆者對(duì)此有所保留。在筆者的團(tuán)隊(duì)中,是不使用Enum作為后綴的,但對(duì)此并不反感。至于枚舉成員名稱(chēng),不用大寫(xiě)字母并下劃線隔開(kāi)的,基本屬于缺心眼行為。Enum的設(shè)計(jì)初衷就是對(duì)常量的規(guī)整和擴(kuò)展,所以命名規(guī)范繼承自常量是比較合理的一種選擇。
在命名中體現(xiàn)設(shè)計(jì)模式,相信這一點(diǎn)很多程序員都能遵守。因?yàn)樵诠P者看來(lái),能在代碼中熟練使用設(shè)計(jì)模式的同學(xué),通常也不會(huì)是一個(gè)對(duì)自己毫無(wú)要求的爛貨。這條命名規(guī)范在Spring以及很多優(yōu)秀的開(kāi)源項(xiàng)目中都有很深刻的體現(xiàn)。
類(lèi)名是否可以使用復(fù)數(shù)形式,相信主要的分歧來(lái)自于工具類(lèi)。筆者的規(guī)定是:
提供一系列靜態(tài)方法的工具類(lèi),一概使用Utils作為后綴命名這條規(guī)范的依據(jù),主要來(lái)自于筆者發(fā)現(xiàn)commons和spring這兩個(gè)比較優(yōu)秀的開(kāi)源框架中提供的工具類(lèi)通常都帶s結(jié)尾。
常量規(guī)約
1.2.1 不允許出現(xiàn)任何魔法值(即未經(jīng)定義的常量)直接出現(xiàn)在代碼中1.2.3 不要使用一個(gè)常量類(lèi)維護(hù)所有常量,應(yīng)該按常量功能進(jìn)行歸類(lèi),分開(kāi)維護(hù)1.2.4 常量的復(fù)用層次有五層:跨應(yīng)用共享常量、應(yīng)用內(nèi)共享常量、子工程內(nèi)共享常量、包內(nèi)共享常量、類(lèi)內(nèi)共享常量1.2.5 如果變量值僅在一個(gè)范圍內(nèi)變化用Enum類(lèi)。如果還帶有名稱(chēng)之外的延伸屬性,必須使用Enum類(lèi)1.1.12 盡量不要在接口里定義變量,如果一定要定義變量,肯定是與接口方法相關(guān),并且是整個(gè)應(yīng)用的基礎(chǔ)常量常量規(guī)約的核心有兩點(diǎn):第一,別使用常量;第二,讓常量可控。
常量的存在按照筆者個(gè)人的理解是向下兼容的選擇(因?yàn)镴DK1.5之后才出現(xiàn)枚舉)外加用起來(lái)足夠爽(想象一下靜態(tài)調(diào)用時(shí)引用的便捷性,甚至基本類(lèi)型可以直接參與業(yè)務(wù)邏輯的計(jì)算)。
所以在上述規(guī)則中,我們可以看到常量進(jìn)化到枚舉的趨勢(shì),也能看到由于用起來(lái)足夠爽帶來(lái)的常量管理需求:要求分組(1.2.3)以及要求放在合適的位置(1.2.4)。
有關(guān)分組,筆者有不同意見(jiàn):常量分組未必要分散到不同的類(lèi),在一個(gè)常量類(lèi)中定義靜態(tài)類(lèi)也是一種分組方式,有時(shí)候這樣的分組方式可能管理起來(lái)更有效。
至于接口中只能定義常量不能定義變量,基本就屬于幼兒園規(guī)則了。
語(yǔ)法糖
1.2.2 long或者Long初始賦值時(shí),必須使用大寫(xiě)的L,不能是小寫(xiě)的l,小寫(xiě)容易跟數(shù)字1混淆,造成誤解1.1.12 接口類(lèi)中的方法和屬性不要加任何修飾符號(hào)(public 也不要加),保持代碼的簡(jiǎn)潔性,并加上有效的Javadoc注釋1.4.2 所有的覆寫(xiě)方法,必須加@Override注解1.4.3 可變參數(shù)必須放置在參數(shù)列表的最后。(提倡同學(xué)們盡量不用可變參數(shù)編程)1.4.4 對(duì)外暴露的接口簽名,原則上不允許修改方法簽名,避免對(duì)接口調(diào)用方產(chǎn)生影響。接口過(guò)時(shí)必須加@Deprecated注解,并清晰地說(shuō)明采用的新接口或者新服務(wù)是什么1.4.5 不能使用過(guò)時(shí)的類(lèi)或方法1.4.10 序列化類(lèi)新增屬性時(shí),請(qǐng)不要修改serialVersionUID字段,避免反序列失敗1.4.17 循環(huán)體內(nèi),字符串的聯(lián)接方式,使用StringBuilder的append1.4.18 final可提高程序響應(yīng)效率1.4.19 慎用Object的clone方法來(lái)拷貝對(duì)象有關(guān)語(yǔ)法糖的總結(jié)其實(shí)比較牽強(qiáng),因?yàn)榻^大多數(shù)的規(guī)則看上去都比較小兒科,比如像覆寫(xiě)方法的@Override注解,@Deprecated注解,可變參數(shù)的問(wèn)題等等基本上都在IDE層面解決了。
當(dāng)一個(gè)項(xiàng)目在IDE中產(chǎn)生了一些由于使用過(guò)時(shí)方法之類(lèi)的事兒導(dǎo)致的warning時(shí),有潔癖的程序員應(yīng)該主動(dòng)修復(fù)這個(gè)warning。這也是是一個(gè)程序員的基本素養(yǎng)問(wèn)題。
最后的三條,筆者認(rèn)為有點(diǎn)雞肋,對(duì)于初級(jí)程序員,大多還到不了考慮final和clone的層次;而中高級(jí)程序員,這幾條規(guī)則對(duì)他們而言并無(wú)問(wèn)題。
基本類(lèi)型
1.2.2 long或者Long初始賦值時(shí),必須使用大寫(xiě)的L,不能是小寫(xiě)的l,小寫(xiě)容易跟數(shù)字1混淆,造成誤解1.4.7 所有的相同類(lèi)型的包裝類(lèi)對(duì)象之間值的比較,全部使用equals方法比較1.4.8 所有的POJO類(lèi)屬性必須使用包裝數(shù)據(jù)類(lèi)型1.4.8 RPC方法的返回值和參數(shù)必須使用包裝數(shù)據(jù)類(lèi)型1.4.8 所有的局部變量【推薦】使用基本數(shù)據(jù)類(lèi)型有關(guān)基本類(lèi)型的聲明(1.2.2)和比較(1.4.7),這兩條規(guī)則比較直觀,不再敘述。
而有關(guān)基本類(lèi)型和包裝類(lèi)型的使用,這東西一直是吵架的核心。用還是不用?這是個(gè)問(wèn)題!很顯然,阿里同學(xué)的觀點(diǎn)是:為了提高程序的容錯(cuò)性和擴(kuò)展性,盡可能使用包裝類(lèi)型。
從阿里同學(xué)舉的例子來(lái)說(shuō),也是有一定說(shuō)服力的:
數(shù)據(jù)庫(kù)的查詢(xún)結(jié)果可能是null,因?yàn)樽詣?dòng)拆箱,用基本數(shù)據(jù)類(lèi)型接收有NPE風(fēng)險(xiǎn)比如顯示成交總額漲跌情況,即正負(fù)x%,x為基本數(shù)據(jù)類(lèi)型,調(diào)用的RPC服務(wù),調(diào)用不成功時(shí),返回的是默認(rèn)值,頁(yè)面顯示:0%,這是不合理的,應(yīng)該顯示成中劃線-。所以包裝數(shù)據(jù)類(lèi)型的null值,能夠表示額外的信息
不過(guò)筆者認(rèn)為,如果程序員對(duì)程序能夠駕馭得比較好,基本類(lèi)型也是一種很好的選擇。因?yàn)榛绢?lèi)型有一些比較好用的特性:比如說(shuō)默認(rèn)值。筆者在這里也舉個(gè)例子進(jìn)行說(shuō)明:
通常我們都會(huì)用is_disabled字段在數(shù)據(jù)庫(kù)中表示某一個(gè)表的記錄是否被邏輯刪除,而這個(gè)字段,在Java代碼中被映射成什么類(lèi)型呢?Boolean?如果被映射成包裝類(lèi)型,那么數(shù)據(jù)庫(kù)里面的這個(gè)字段就可以為null,有些讀者會(huì)說(shuō),這并沒(méi)有什么問(wèn)題啊。可是,數(shù)據(jù)庫(kù)is_disabled字段如果為null,代表什么邏輯含義呢?這條記錄究竟是有效還是無(wú)效?如果這個(gè)字段不能為null,那么將其映射成基本類(lèi)型是一個(gè)皆大歡喜的事情:既保證了數(shù)據(jù)庫(kù)數(shù)據(jù)的完整性,我們?cè)诔跏蓟臅r(shí)候還可以忽略這個(gè)字段,因?yàn)閎oolean天生的默認(rèn)值就是false
所以,筆者對(duì)于包裝類(lèi)還是基本類(lèi)型的結(jié)論是:
一切跟著業(yè)務(wù)的實(shí)際情況而定,基本類(lèi)型也有其生存空間方法命名
1.1.15 Service/DAO層方法命名規(guī)約- 獲取單個(gè)對(duì)象的方法用get做前綴- 獲取多個(gè)對(duì)象的方法用list做前綴- 獲取統(tǒng)計(jì)值的方法用count做前綴- 插入的方法用save(推薦)或insert做前綴- 刪除的方法用remove(推薦)或delete做前綴- 修改的方法用update做前綴有關(guān)方法的命名,筆者想多說(shuō)幾句不同意見(jiàn)。對(duì)于上述的規(guī)則,筆者認(rèn)為適合在DAO這個(gè)層次進(jìn)行實(shí)踐,而不能應(yīng)用于Service層。
使用Hibernate作為持久層框架的讀者,對(duì)Hibernate的API應(yīng)該比較熟悉,而上面的命名規(guī)范,和Hibernate對(duì)外暴露的API名稱(chēng)是很接近的。我們知道,通常到了DAO這個(gè)層次,數(shù)據(jù)庫(kù)操作相對(duì)來(lái)說(shuō)是一個(gè)原子操作,所以增刪改查的語(yǔ)義是最適合做方法命名的。這也就是筆者認(rèn)為這套規(guī)則在DAO層能夠被實(shí)踐得很好的一個(gè)原因。
當(dāng)然,上述規(guī)則中有一個(gè)例外:
獲取單個(gè)對(duì)象用load做前綴,避免使用get原因很簡(jiǎn)單,get可能是getter方法的前綴,作為一個(gè)偏執(zhí)狂,老子不冒風(fēng)險(xiǎn)。
話題回到Service的命名上來(lái),為什么筆者不認(rèn)同使用相同的命名規(guī)范作用于Service層呢?因?yàn)镾ervice層通常是對(duì)外暴露的接口,具有一定的業(yè)務(wù)意義,也就是說(shuō)Service層通常也不會(huì)是簡(jiǎn)單的增刪改查,而是若干原子操作的集合。
舉兩個(gè)很簡(jiǎn)單的例子:發(fā)短信。發(fā)短信這個(gè)業(yè)務(wù)中可能包含了本地配置的讀取、本地?cái)?shù)據(jù)庫(kù)的讀寫(xiě),遠(yuǎn)程服務(wù)的調(diào)用。我們可以看到這是一連串?dāng)?shù)據(jù)庫(kù)操作甚至是異構(gòu)系統(tǒng)調(diào)用的集合實(shí)現(xiàn),能用簡(jiǎn)單的增刪改查來(lái)命名嗎?所以,筆者的觀點(diǎn)很簡(jiǎn)單:
Service層接口方法的命名,應(yīng)還原業(yè)務(wù)的本來(lái)面目,采用動(dòng)詞或者動(dòng)賓結(jié)構(gòu)來(lái)進(jìn)行方法的命名舉例來(lái)說(shuō):resetPassword / login / sendMessage 都是比較合理的命名方式。
方法和屬性
1.4.9 定義DO/DTO/VO等POJO類(lèi)時(shí),不要設(shè)定任何屬性默認(rèn)值1.4.11 構(gòu)造方法里面禁止加入任何業(yè)務(wù)邏輯,如果有初始化邏輯,請(qǐng)放在init方法中1.4.14 當(dāng)一個(gè)類(lèi)有多個(gè)構(gòu)造方法,或者多個(gè)同名方法,這些方法應(yīng)該按順序放置在一起,便于閱讀1.4.15 類(lèi)內(nèi)方法定義順序依次是:公有方法或保護(hù)方法 > 私有方法 > getter/setter方法1.4.16 setter方法中,參數(shù)名稱(chēng)與類(lèi)成員變量名稱(chēng)一致,this.成員名=參數(shù)名。在getter/setter方法中,盡量不要增加業(yè)務(wù)邏輯1.4.20 類(lèi)成員與方法訪問(wèn)控制從嚴(yán)這幾條規(guī)約理解起來(lái)不難,執(zhí)行起來(lái)也不難。要探究背后的原因,可能就需要花點(diǎn)功夫。
比如,構(gòu)造方法和setter/getter方法禁止加入業(yè)務(wù)邏輯,主要是這些方法有很大概率被程序框架的反射機(jī)制調(diào)用。一旦其中含有業(yè)務(wù)邏輯,那么調(diào)試和定位就會(huì)變成災(zāi)難。
不過(guò)對(duì)于getter方法,通常要網(wǎng)開(kāi)一面。因?yàn)樵趯?shí)際情況中,我們往往會(huì)在一個(gè)POJO中加入額外的getter方法用于序列化或者內(nèi)部邏輯的使用。在這種情況下,避免和其他getter方法產(chǎn)生分歧是需要注意的地方。
至于說(shuō)到類(lèi)內(nèi)的方法定義順序,筆者基本同意上述規(guī)則,但在實(shí)際執(zhí)行中可能更加嚴(yán)格:getter和setter方法的順序也有嚴(yán)格講究,必須是先getter方法,后setter方法,而不是讓它們成對(duì)出現(xiàn)。
有關(guān)類(lèi)成員和方法的訪問(wèn)控制,阿里的同學(xué)洋洋灑灑說(shuō)了好幾條,語(yǔ)法層面偏多,在這里就不再詳細(xì)展開(kāi)。
格式規(guī)約
格式規(guī)約是代碼規(guī)范中爭(zhēng)議最大的,由于條目眾多,在這里就不逐一解讀,挑選幾條大致說(shuō)一下。
1.3.5 縮進(jìn)采用4個(gè)空格,禁止使用tab字符1.3 6. 單行字符數(shù)限不超過(guò) 120 個(gè)1.3.8 IDE的text file encoding設(shè)置為UTF-8; IDE中文件的換行符使用Unix格式,不要使用windows格式1.3.10 方法體內(nèi)的執(zhí)行語(yǔ)句組、變量的定義語(yǔ)句組、不同的業(yè)務(wù)邏輯之間或者不同的語(yǔ)義之間插入一個(gè)空行絕大多數(shù)情況下,空格黨和Tab黨的較量是空格黨完勝。筆者也不記得是多少年前被一位前輩教育說(shuō)禁止使用Tab,就保持了良好的習(xí)慣至今。對(duì)于縮進(jìn),個(gè)人比較贊同4個(gè)空格,但HTML等頁(yè)面上使用4個(gè)空格的話,一些復(fù)雜頁(yè)面的縮進(jìn)就會(huì)比較恐怖,此時(shí)可以降級(jí)為2個(gè)空格。
對(duì)于單行字符數(shù)的限制不超過(guò)120個(gè)這條規(guī)則,筆者完全不能認(rèn)同。這里面牽涉到的情況比較多,不能一棒子打死了。有些邏輯有大量的分支和循環(huán)的嵌套,如果遵循4個(gè)空格的縮進(jìn)原則,碰到方法名稱(chēng)還比較長(zhǎng)的狀況,就要折行,這給代碼閱讀帶來(lái)極大的困擾;另外有一種情況,就是需要額外進(jìn)行比較長(zhǎng)的注釋編寫(xiě),不能寫(xiě)在一行里的感覺(jué)真是比較糟糕,因?yàn)檫€得考慮斷句才不影響閱讀。另外,筆者有一個(gè)習(xí)慣是在條件語(yǔ)句邊上加一句注釋,這樣就有很大概率會(huì)超出120字:
有人會(huì)問(wèn),條件語(yǔ)句邊上加注釋是什么鬼?從上面的代碼上可以看到,條件語(yǔ)句上面的一行注釋實(shí)際上在解釋整個(gè)代碼片段,而條件語(yǔ)句邊上的注釋說(shuō)明的是條件語(yǔ)句本身!當(dāng)然如果讀者有更好的寫(xiě)注釋的位置,請(qǐng)及時(shí)給筆者留言。
文件的UTF-8和Unix格式?jīng)]什么好說(shuō)的,IDE支持的也很好。但這一點(diǎn)對(duì)初級(jí)程序員尤為重要,我已經(jīng)不知道多少次就這個(gè)問(wèn)題懲罰過(guò)實(shí)習(xí)生了。
有關(guān)語(yǔ)句組空行,是筆者極力推薦的一個(gè)做法。這不僅僅是為了空行而空行,這里的空行本身就是一種編程思路的整理。而筆者還有一個(gè)習(xí)慣就是對(duì)比較復(fù)雜的邏輯,都在語(yǔ)句組的前面加上注釋,注釋也用編號(hào)編排,這樣回頭debug時(shí)也會(huì)極大提升效率。
集合類(lèi)型
阿里規(guī)范中的集合類(lèi)型這個(gè)章節(jié),感覺(jué)寫(xiě)得比較雞肋。絕大多數(shù)的規(guī)范似乎都是針對(duì)初級(jí)程序員的。筆者看了半天也沒(méi)總結(jié)出一條值得額外解讀的,所以權(quán)當(dāng)復(fù)習(xí)一遍基礎(chǔ)知識(shí)就好。
并發(fā)處理
1.6.1 獲取單例對(duì)象需要保證線程安全,其中的方法也要保證線程安全1.6.2 創(chuàng)建線程或線程池時(shí)請(qǐng)指定有意義的線程名稱(chēng),方便出錯(cuò)時(shí)回溯1.6.3 線程資源必須通過(guò)線程池提供,不允許在應(yīng)用中自行顯式創(chuàng)建線程1.6.4 線程池不允許使用Executors去創(chuàng)建,而是通過(guò)ThreadPoolExecutor去創(chuàng)建上面這4條規(guī)則主要是針對(duì)線程的創(chuàng)建和使用。由于Spring的存在,其實(shí)上述情況不太可能發(fā)生。
1.6.5 高并發(fā)時(shí),同步調(diào)用應(yīng)該去考量鎖的性能損耗。能用無(wú)鎖數(shù)據(jù)結(jié)構(gòu),就不要用鎖;能鎖區(qū)塊,就不要鎖整個(gè)方法體;能用對(duì)象鎖,就不要用類(lèi)鎖1.6.7 對(duì)多個(gè)資源、數(shù)據(jù)庫(kù)表、對(duì)象同時(shí)加鎖時(shí),需要保持一致的加鎖順序,否則可能會(huì)造成死鎖1.6.8 并發(fā)修改同一記錄時(shí),避免更新丟失,要么在應(yīng)用層加鎖,要么在緩存加鎖,要么在數(shù)據(jù)庫(kù)層使用樂(lè)觀鎖,使用version作為更新依據(jù)上面這3條規(guī)則主要是針對(duì)鎖。不過(guò)這幾條規(guī)則看上去更像是3道面試題的答案。這3道面試題分別是:
使用鎖同步,有什么需要注意的地方?什么是死鎖?舉例說(shuō)明什么情況會(huì)發(fā)生死鎖?什么是樂(lè)觀鎖?什么是悲觀鎖?分別用在什么場(chǎng)景?相信能解答上述面試題的同學(xué),應(yīng)該對(duì)上面的原則了然于心。
1.6.9 多線程并行處理定時(shí)任務(wù)時(shí),Timer運(yùn)行多個(gè)TimeTask時(shí),只要其中之一沒(méi)有捕獲拋出的異常,其它任務(wù)便會(huì)自動(dòng)終止運(yùn)行,使用ScheduledExecutorService則沒(méi)有這個(gè)問(wèn)題1.6.10 使用CountDownLatch進(jìn)行異步轉(zhuǎn)同步操作,每個(gè)線程退出前必須調(diào)用countDown方法,線程執(zhí)行代碼注意catch異常,確保countDown方法可以執(zhí)行,避免主線程無(wú)法執(zhí)行至countDown方法,直到超時(shí)才返回結(jié)果回溯1.6.13 volatile解決多線程內(nèi)存不可見(jiàn)問(wèn)題。對(duì)于一寫(xiě)多讀,是可以解決變量同步問(wèn)題,但是如果多寫(xiě),同樣無(wú)法解決線程安全問(wèn)題1.6.14 HashMap在容量不夠進(jìn)行resize時(shí)由于高并發(fā)可能出現(xiàn)死鏈1.6.15 ThreadLocal無(wú)法解決共享對(duì)象的更新問(wèn)題,ThreadLocal對(duì)象建議使用static修飾。這個(gè)變量是針對(duì)一個(gè)線程內(nèi)所有操作共有的,所以設(shè)置為靜態(tài)變量,
所有此類(lèi)實(shí)例共享此靜態(tài)變量
上面這些規(guī)則基本上屬于知識(shí)貼范疇,可以一帶而過(guò),有些可能不太會(huì)實(shí)際碰到。像定時(shí)任務(wù),可能使用Spring的封裝更多一些,而Spring默認(rèn)就是使用ScheduledExecutorService的。
而CountDownLatch的異常捕獲,也是一個(gè)老生常談的問(wèn)題了,屬于多線程編程的基本功。
最后的三條對(duì)于寫(xiě)應(yīng)用的同學(xué)接觸不多,但寫(xiě)底層的同學(xué)應(yīng)該會(huì)很熟悉。
注釋規(guī)約
注釋規(guī)約的內(nèi)容比較多,這里也僅挑選一些具有代表性的進(jìn)行解讀?
1.8.5 所有的枚舉類(lèi)型字段必須要有注釋,說(shuō)明每個(gè)數(shù)據(jù)項(xiàng)的用途1.8.6 與其"半吊子"英文來(lái)注釋,不如用中文注釋把問(wèn)題說(shuō)清楚。專(zhuān)有名詞與關(guān)鍵字保持英文原文即可1.8.8 注釋掉的代碼盡量要配合說(shuō)明,而不是簡(jiǎn)單的注釋掉1.8.10 好的命名、代碼結(jié)構(gòu)是自解釋的,注釋力求精簡(jiǎn)準(zhǔn)確、表達(dá)到位枚舉類(lèi)加注釋是非常必要的,因?yàn)槊杜e通常是都是常量的擴(kuò)展,而常量是需要說(shuō)明的。
鑒于很多程序員的英語(yǔ)水平,筆者建議英語(yǔ)不夠好的程序員直接使用中文寫(xiě)注釋。
對(duì)于注釋掉的代碼,筆者的意見(jiàn)是在絕大多數(shù)情況下應(yīng)該直接刪除,除非在很短的時(shí)間內(nèi)還有恢復(fù)的余地。
有關(guān)什么是好的命名和代碼結(jié)構(gòu),什么樣的命名能夠使得代碼做到自解釋,筆者將另外撰文進(jìn)行說(shuō)明。
數(shù)據(jù)庫(kù)規(guī)約
數(shù)據(jù)庫(kù)規(guī)約本身并不屬于Java規(guī)約的范疇,不過(guò)阿里的規(guī)范中包含了不少數(shù)據(jù)庫(kù)規(guī)約的內(nèi)容,所以筆者也同樣加以解讀。
3.1.1 是與否概念的字段,必須使用is_xxx的方式命名,數(shù)據(jù)類(lèi)型是unsigned tinyint( 1表示是,0表示否)3.1 2 表名、字段名必須使用小寫(xiě)字母或數(shù)字;禁止出現(xiàn)數(shù)字開(kāi)頭,禁止兩個(gè)下劃線中間只出現(xiàn)數(shù)字3.1.3 表名不使用復(fù)數(shù)名詞3.1.4 禁用保留字3.1.5 唯一索引名為uk_字段名;普通索引名則為idx_字段名3.1.10 表的命名最好是加上業(yè)務(wù)名稱(chēng)_表的作用3.1.11 庫(kù)名與應(yīng)用名稱(chēng)盡量一致上述規(guī)約主要說(shuō)的是庫(kù)、表、字段的命名規(guī)約。應(yīng)該說(shuō)絕大多數(shù)的上述規(guī)約都是參考項(xiàng),需要根據(jù)實(shí)際情況進(jìn)行調(diào)整,我們逐條來(lái)說(shuō)。
有關(guān)布爾值的數(shù)據(jù)庫(kù)映射,對(duì)于使用is_xx進(jìn)行命名沒(méi)有異議,對(duì)于數(shù)據(jù)類(lèi)型是否應(yīng)該使用tinyint稍有保留,筆者實(shí)際上使用bit更多。由于布爾值所對(duì)應(yīng)的Java類(lèi)型是boolean,所以筆者通常在命名時(shí),利用boolean的默認(rèn)值特性,對(duì)一些常用的命名進(jìn)行更加嚴(yán)格的規(guī)定。比如「是否有效」,命名成為「is_disabled」就要比「is_enabled」來(lái)的好。因?yàn)?is_disable = false 是絕大多數(shù)程序的事實(shí)邏輯,這樣就可以利用boolean值默認(rèn)為false的特性。
Java中的絕大多數(shù)命名都使用駝峰法,而數(shù)據(jù)庫(kù)的命名實(shí)際上更加嚴(yán)格。光光小寫(xiě)是不夠的,而是要強(qiáng)制使用下劃線命名法(主要是因?yàn)镾QL是大小寫(xiě)不敏感的語(yǔ)言)。筆者在實(shí)際工作中經(jīng)??吹绞褂民劮宸砻蛘咦侄蚊?#xff0c;這種基本上屬于小學(xué)沒(méi)畢業(yè)的行為。
有關(guān)表名不能使用復(fù)數(shù),不能使用關(guān)鍵字,這些屬于比較基礎(chǔ)的命名規(guī)范,應(yīng)該遵守。但是筆者在這里提出更為嚴(yán)格的要求:不僅不能使用SQL關(guān)鍵字進(jìn)行命名,同樣不允許使用Java關(guān)鍵字!因?yàn)榻^大多數(shù)情況,數(shù)據(jù)庫(kù)字段會(huì)被映射到相應(yīng)的Java對(duì)象,如果可以使用Java關(guān)鍵字,那么映射的時(shí)候就是自找麻煩了。
最后三條規(guī)約屬于建議,相信每個(gè)公司都有自己獨(dú)特的規(guī)定。比如筆者見(jiàn)過(guò)有一些寫(xiě)Oracle出身的程序員,習(xí)慣使用tbl_做表名的前綴,使用vw_做視圖的前綴。個(gè)人覺(jué)得這個(gè)方面不宜做過(guò)多規(guī)定,只要團(tuán)隊(duì)保持風(fēng)格整體一致即可。
3.1.6 小數(shù)類(lèi)型為decimal,禁止使用float和double3.1.7 如果存儲(chǔ)的字符串長(zhǎng)度幾乎相等,使用char定長(zhǎng)字符串類(lèi)型3.1.8 varchar是可變長(zhǎng)字符串,不預(yù)先分配存儲(chǔ)空間,長(zhǎng)度不要超過(guò)5000這三條主要說(shuō)的是數(shù)據(jù)庫(kù)設(shè)計(jì)時(shí)的類(lèi)型規(guī)約。
除了上述三條之外,在筆者團(tuán)隊(duì)另外還會(huì)遵守如下幾條:
明確日期和時(shí)間,日期使用date類(lèi)型并使用xxDate進(jìn)行Java字段命名,時(shí)間使用date_time類(lèi)型并使用xxTime進(jìn)行Java字段命名,以示區(qū)分上面這條主要是和日期時(shí)間有關(guān)的,強(qiáng)制這樣的規(guī)約,對(duì)于提升代碼的可讀性是有幫助的。
枚舉類(lèi)型在數(shù)據(jù)庫(kù)中既可以映射成int,也可以映射成varchar,視實(shí)際情況定通常對(duì)于排序和檢索有強(qiáng)依賴(lài)的,枚舉類(lèi)型映射成int比較理想,否則可以映射成varchar。雖然從效率上說(shuō),int基本上會(huì)強(qiáng)于varchar,但varchar畢竟可讀性更好,所以還是應(yīng)該一分為二來(lái)看。
3.1.9 表必備三字段:id, gmt_create, gmt_modified3.1.8 如果存儲(chǔ)長(zhǎng)度大于此值,定義字段類(lèi)型為text,獨(dú)立出來(lái)一張表,用主鍵來(lái)對(duì)應(yīng),避免影響其它字段索引效率3.1.12 如果修改字段含義或?qū)ψ侄伪硎镜臓顟B(tài)追加時(shí),需要及時(shí)更新字段注釋3.1.13 字段允許適當(dāng)冗余,以提高性能,但是必須考慮數(shù)據(jù)同步的情況3.1.14 單表行數(shù)超過(guò)500萬(wàn)行或者單表容量超過(guò)2GB,才推薦進(jìn)行分庫(kù)分表3.1.15 合適的字符存儲(chǔ)長(zhǎng)度,不但節(jié)約數(shù)據(jù)庫(kù)表空間、節(jié)約索引存儲(chǔ),更重要的是提升檢索速度上面的規(guī)約主要涉及到一些數(shù)據(jù)庫(kù)表設(shè)計(jì)上的原則。
其中,3.1.8是非常值得大家注意的一點(diǎn),筆者個(gè)人的習(xí)慣是對(duì)于大字段,拆表的同時(shí),優(yōu)化SQL,盡可能做到用主鍵單獨(dú)取大字段,避免產(chǎn)生效率瓶頸。
而3.1.14是希望提醒一些自視甚高的架構(gòu)師不要過(guò)早的進(jìn)行過(guò)度設(shè)計(jì)。這里筆者提一點(diǎn):
對(duì)于每一張數(shù)據(jù)庫(kù)表的設(shè)計(jì),應(yīng)該預(yù)估表在未來(lái)若干時(shí)間段內(nèi)的數(shù)量,以采取最佳的程序處理措施這里所說(shuō)的最佳程序處理措施包括并不限于:使用應(yīng)用級(jí)別緩存對(duì)數(shù)據(jù)庫(kù)進(jìn)行減壓;選取合適的時(shí)間點(diǎn)對(duì)表進(jìn)行分庫(kù)分表;是否進(jìn)行人為拆表以保證較快的SQL執(zhí)行等等。
有關(guān)3.1.13,我們?cè)谟嘘P(guān)SQL編寫(xiě)環(huán)節(jié)還會(huì)說(shuō)到。
3.2.1 業(yè)務(wù)上具有唯一特性的字段,即使是組合字段,也必須建成唯一索引3.2.2 超過(guò)三個(gè)表禁止join3.2.3 在varchar字段上建立索引時(shí),必須指定索引長(zhǎng)度3.2.4 頁(yè)面搜索嚴(yán)禁左模糊或者全模糊3.2.5 如果有order by的場(chǎng)景,請(qǐng)注意利用索引的有序性3.2.7 利用延遲關(guān)聯(lián)或者子查詢(xún)優(yōu)化超多分頁(yè)場(chǎng)景3.2.9 建組合索引的時(shí)候,區(qū)分度最高的在最左邊上述規(guī)約主要講的是和索引相關(guān)的內(nèi)容。對(duì)于這塊,筆者不是專(zhuān)業(yè)的DBA,所以只是挑了其中和程序開(kāi)發(fā)特別有關(guān)的來(lái)講一講。
比如3.2.2的禁止超過(guò)3個(gè)表的join,在筆者的團(tuán)隊(duì)中,規(guī)定更為嚴(yán)格:
禁止超過(guò)2個(gè)表的join語(yǔ)句出現(xiàn)在程序中其實(shí)不許使用join是很多初級(jí)程序員非常不能理解的。要說(shuō)明白這個(gè)問(wèn)題,估計(jì)又要長(zhǎng)篇大論,筆者會(huì)另辟文章進(jìn)行說(shuō)明。但這里還是引用一下robbin的觀點(diǎn)(筆者表示深刻贊同):
另外有關(guān)嚴(yán)禁使用全模糊查找,建組合索引時(shí),區(qū)分度最高的往左放這些原則,在一定程度上會(huì)改變我們編寫(xiě)程序的習(xí)慣,所以應(yīng)該時(shí)刻注意。
3.3.1 不要使用count(列名)或count(常量)來(lái)替代count(*)3.3.5 在代碼中寫(xiě)分頁(yè)查詢(xún)邏輯時(shí),若count為0應(yīng)直接返回,避免執(zhí)行后面的分頁(yè)語(yǔ)句3.3.6 不得使用外鍵與級(jí)聯(lián),一切外鍵概念必須在應(yīng)用層解決3.3.7 禁止使用存儲(chǔ)過(guò)程,存儲(chǔ)過(guò)程難以調(diào)試和擴(kuò)展,更沒(méi)有移植性3.3.9 in操作能避免則避免,若實(shí)在避免不了,需要仔細(xì)評(píng)估in后邊的集合元素?cái)?shù)量,控制在1000個(gè)之內(nèi)3.4.1 在表查詢(xún)中,一律不要使用 * 作為查詢(xún)的字段列表,需要哪些字段必須明確寫(xiě)明上面的這幾條屬于SQL編寫(xiě)規(guī)約。阿里的規(guī)范中洋洋灑灑講了很多條,實(shí)際上都是在給程序員提個(gè)醒,筆者在這里不在贅述
有關(guān)count(*)的爭(zhēng)論,一直有大量的說(shuō)法。此次阿里的規(guī)范總算為count(*)黨找到了SQL標(biāo)準(zhǔn),應(yīng)該說(shuō)也基本為這件事情畫(huà)上了句號(hào)。
有關(guān)外鍵和級(jí)聯(lián),筆者稍有困惑的是外鍵。因?yàn)榘凑展P者的理解,外鍵影響數(shù)據(jù)庫(kù)插入的速度應(yīng)該有限,與外鍵約束帶來(lái)的好處相比,或許還是有外鍵更好一些(有這方面經(jīng)驗(yàn)的讀者可以留言指點(diǎn)迷津)。級(jí)聯(lián)是惡魔,必須禁止。
至于存儲(chǔ)過(guò)程,或許Oracle出身的DBA會(huì)跳出來(lái)唱反調(diào)了。筆者的觀點(diǎn)和阿里相同:存儲(chǔ)過(guò)程很難移植和維護(hù),應(yīng)該拋棄。
有關(guān)表查詢(xún)中不許使用 * 作為查詢(xún)的字段列表,這點(diǎn)或許能夠成為規(guī)約,但筆者并不十分認(rèn)同。尤其是對(duì)于使用Hibernate作為ORM工具的同學(xué)來(lái)說(shuō),這條規(guī)則執(zhí)行起來(lái)有難度。
代碼風(fēng)格
1.7.1 在一個(gè)switch塊內(nèi),每個(gè)case要么通過(guò)break/return等來(lái)終止,要么注釋說(shuō)明程序?qū)⒗^續(xù)執(zhí)行到哪一個(gè)case為止;在一個(gè)switch塊內(nèi),都必須包含一個(gè)default語(yǔ)句并且放在最后,即使它什么代碼也沒(méi)有
這條主要是期待程序員人為把握好代碼的執(zhí)行邏輯。對(duì)于switch語(yǔ)句,如果沒(méi)有終止語(yǔ)句,會(huì)依次執(zhí)行每一個(gè)case塊。實(shí)際上,筆者認(rèn)為switch語(yǔ)句是一個(gè)比較差的語(yǔ)法糖,通常情況下都可以用更加優(yōu)雅的方式來(lái)寫(xiě),包括并不限于使用設(shè)計(jì)模式。所以在筆者的團(tuán)隊(duì)中是禁止使用switch語(yǔ)句的。
1.7 2 在if/else/for/while/do語(yǔ)句中必須使用大括號(hào),即使只有一行代碼,避免使用下面的形式:if (condition) statements;這一條比較有意思,因?yàn)檫@種一行式的代碼風(fēng)格,在javascript里面經(jīng)常會(huì)看到,所以很多全棧工程師也會(huì)把它引入到Java中來(lái)。筆者對(duì)此并不反感,但確實(shí)在可讀性上不那么友好。
1.7.3 推薦盡量少用else, if-else的方式可以改寫(xiě)成: if(condition){ ... return obj; } // 接著寫(xiě)else的業(yè)務(wù)邏輯代碼;1.7.3 如果非得使用if()...else if()...else...方式表達(dá)邏輯,【強(qiáng)制】請(qǐng)勿超過(guò)3層,超過(guò)請(qǐng)使用狀態(tài)設(shè)計(jì)模式上面這點(diǎn)筆者比較認(rèn)同,因?yàn)閑lse不僅會(huì)帶來(lái)大段的代碼縮進(jìn)的困擾,同時(shí)也會(huì)降低代碼的可讀性。不過(guò)對(duì)于那些堅(jiān)持必須在代碼的最后一行統(tǒng)一return的同學(xué),上面的寫(xiě)法可能就不太容易接受了。實(shí)際上,上述代碼結(jié)構(gòu)比較常見(jiàn)于Spring的源碼中,倒不是盡早return,而是else的邏輯塊可能直接throw異常出去了。
1.7.4 除常用方法(如getXxx/isXxx)等外,不要在條件判斷中執(zhí)行其它復(fù)雜的語(yǔ)句,將復(fù)雜邏輯判斷的結(jié)果賦值給一個(gè)有意義的布爾變量名,以提高可讀性 boolean existed = (file.open(fileName, "w") != null) && (...) || (...); if (existed) { ... }有關(guān)這一條,補(bǔ)充說(shuō)明一下:將復(fù)雜的邏輯判斷結(jié)果賦值給一個(gè)有意義的布爾變量名,除了提高可讀性之外,實(shí)際上能夠極大方便調(diào)試。但筆者認(rèn)為單單只是抽取部分代碼,并不能提高可讀性,而是應(yīng)該將復(fù)雜的邏輯判斷進(jìn)一步封裝為一個(gè)方法:
上面的代碼片段中,左邊是阿里風(fēng)格,右邊是陸老師的風(fēng)格,大家可以比較一下,哪個(gè)更好?哪個(gè)更符合面向?qū)ο蟮乃季S呢?
1.7.5 循環(huán)體中的語(yǔ)句要考量性能,以下操作盡量移至循環(huán)體外處理,如定義對(duì)象、變量、獲取數(shù)據(jù)庫(kù)連接,進(jìn)行不必要的try-catch操作這一條值得說(shuō)一下,因?yàn)橛行┐a會(huì)走得比較深,寫(xiě)著寫(xiě)著就忘了它處于循環(huán)體的內(nèi)部了。所以保持一個(gè)謹(jǐn)慎的心態(tài)比較重要。
1.7.7 方法中需要進(jìn)行參數(shù)校驗(yàn)的場(chǎng)景: 1) 調(diào)用頻次低的方法。 2) 執(zhí)行時(shí)間開(kāi)銷(xiāo)很大的方法,參數(shù)校驗(yàn)時(shí)間幾乎可以忽略不計(jì),但如果因?yàn)閰?shù)錯(cuò)誤導(dǎo)致中間執(zhí)行回退,或者錯(cuò)誤,那得不償失。 3) 需要極高穩(wěn)定性和可用性的方法。 4) 對(duì)外提供的開(kāi)放接口,不管是RPC/API/HTTP接口。 5) 敏感權(quán)限入口。1.7.8 方法中不需要參數(shù)校驗(yàn)的場(chǎng)景: 1) 極有可能被循環(huán)調(diào)用的方法,不建議對(duì)參數(shù)進(jìn)行校驗(yàn)。但在方法說(shuō)明里必須注明外部參數(shù)檢查 2) 底層的方法調(diào)用頻度都比較高,一般不校驗(yàn)。畢竟是像純凈水過(guò)濾的最后一道,參數(shù)錯(cuò)誤不太可能到底層才會(huì)暴露問(wèn)題。一般DAO層與Service層都在同一個(gè)應(yīng)用中,部署在同一臺(tái)服務(wù)器中,所以DAO的參數(shù)校驗(yàn),可以省略 3) 被聲明成private只會(huì)被自己代碼所調(diào)用的方法,如果能夠確定調(diào)用方法的代碼傳入?yún)?shù)已經(jīng)做過(guò)檢查或者肯定不會(huì)有問(wèn)題,此時(shí)可以不校驗(yàn)參數(shù)
這兩條說(shuō)的是參數(shù)校驗(yàn),說(shuō)的比較在理,也比較全面。比起很多公司的奇葩規(guī)定來(lái)說(shuō),要人性化得多。筆者認(rèn)為需要補(bǔ)充的是:參數(shù)的校驗(yàn)主要還需要從格式和業(yè)務(wù)兩個(gè)層面進(jìn)行考量。業(yè)務(wù)層面的校驗(yàn)往往要比單純的格式校驗(yàn)更為復(fù)雜,所以在寫(xiě)代碼時(shí)可以建立一定層次的假設(shè),當(dāng)然這可能也會(huì)引入團(tuán)隊(duì)溝通的問(wèn)題,需要根據(jù)實(shí)際情況權(quán)衡。
有關(guān)阿里代碼風(fēng)格方面的解讀,受限于阿里自身提出的規(guī)約比較少。對(duì)此,筆者是稍有失望的。因?yàn)榇a風(fēng)格規(guī)約是最能夠體現(xiàn)一個(gè)團(tuán)隊(duì)對(duì)于代碼整潔程度的一個(gè)衡量標(biāo)準(zhǔn)。所以筆者忍不住在這里多加了幾條筆者團(tuán)隊(duì)的共識(shí),供讀者參考:
在任何情況下,代碼量越少越容易維護(hù)基于上面的原則,筆者的團(tuán)隊(duì)會(huì)鼓勵(lì)使用三目表達(dá)式對(duì)簡(jiǎn)要的if/else進(jìn)行重構(gòu)
當(dāng)然,像下列左側(cè)的代碼,也會(huì)重構(gòu)成右側(cè)的:
?
一個(gè)復(fù)雜的Service層邏輯,不應(yīng)超過(guò)30行,否則需要進(jìn)行邏輯規(guī)整和抽象
在業(yè)務(wù)邏輯中盡可能不要使用setter方法,而是使用構(gòu)造函數(shù)或者封裝成一個(gè)有邏輯意義的方法,提高代碼的可讀性什么?連setter方法都不讓用?這是什么SB一樣的規(guī)約啊!事實(shí)上,筆者團(tuán)隊(duì)確實(shí)是這么做的,我們來(lái)看一下代碼:
?
在上面的代碼中,左側(cè)代碼中的setter方法調(diào)用,會(huì)被封裝到ShuttleOrder對(duì)象中的cancel方法中去。在實(shí)際的service代碼中,只會(huì)出現(xiàn)下半部分的一行代碼。
這樣做的好處在于:cancel這個(gè)方法被封裝后,shuttleOrder.cancel()的調(diào)用從可讀性上要明顯優(yōu)于使用2句setter方法,同時(shí)也為將來(lái)的邏輯擴(kuò)展預(yù)留了位置。這也是面向?qū)ο蟮囊环N實(shí)踐。
?
轉(zhuǎn)載自:https://www.cnblogs.com/winner-0715/p/7594254.html
轉(zhuǎn)載于:https://www.cnblogs.com/renyuanwei/p/9169452.html
總結(jié)
以上是生活随笔為你收集整理的解读阿里官方代码规范的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 61二叉搜索树的第k个结点
- 下一篇: Windows + Ubuntu下JDK