1. 分支開發(fā)流程
- GIT分支開發(fā)規(guī)范,具體請(qǐng)參考:http://www.lxweimin.com/p/cbd8cf5e232d。
2. 代碼規(guī)范
- 主要使用Java語(yǔ)言來(lái)進(jìn)行開發(fā),嚴(yán)格遵循Sun公司的Java編碼規(guī)范。此外還有以下補(bǔ)充。
2.1 大小寫
- 常量的字母 全部大寫,單詞之間用一個(gè) 下劃線字符(_)進(jìn)行分隔。
- 除常量外的命名 采用大小寫混合,提高名字的可讀性。
- 一般采用小寫字母,但是類和接口的名字的首字母,以及任何中間單詞的 首字母應(yīng)該大寫。
2.2 編碼格式
- 確保IDE工作環(huán)境使用UTF-8編碼格式。
- 縮進(jìn)一律使用空格而非Tab。
- 縮進(jìn)一律采用4空格。可以設(shè)置IDE 1Tab=4空格。
- 對(duì)于非第一次創(chuàng)建的代碼文件,禁止做reformate操作,這個(gè)操作會(huì)破壞代碼的修改歷史,造成大概率的push、merge、pull等操作沖突。
2.3 常量定義
- 對(duì)具有特殊意義的數(shù)字或字符要以常量的形式定義,并說(shuō)明常量所表示的意義,避免幻數(shù)的出現(xiàn)。
2.4 統(tǒng)一定義公共常量
- 對(duì)于一些公共常量的使用,應(yīng)該單獨(dú)定義相應(yīng)的常量類,并統(tǒng)一使用,避免在各業(yè)務(wù)類、功能類中分別定義、直接使用。
2.5 命名規(guī)則
Package:
1.1 Package: 每一個(gè)包的名稱總是小寫,暫定將com.keegoo.+(模塊名)作為總前綴,比如com.keegoo.demo.blog.service、com.keegoo.demo.blog.dto;
1.2 明確不同業(yè)務(wù)建議建立子package,比如有關(guān)User業(yè)務(wù)的:com.keegoo.user.account.service、com.keegoo.user.account.dto;Class類:類名必須是名詞,且以大寫字母開頭。類名應(yīng)該簡(jiǎn)單清晰。
Interface類:
3.1 Interface命名上,暫定以XxxService的形式提供;
3.2 最終提供的接口應(yīng)以Java Interface的形式出現(xiàn);
3.3 每個(gè)獨(dú)立的業(yè)務(wù)的Interface應(yīng)該有獨(dú)立的package包,一個(gè)package下可包含該業(yè)務(wù)模塊下的多個(gè)Interface;
3.4 每個(gè)Interface的一個(gè)方法對(duì)應(yīng)所謂的一個(gè)“中間層服務(wù)接口”,需要按照規(guī)范書寫該接口的說(shuō)明;緩存命名規(guī)則:對(duì)緩存的key要采用命名空間,以避免沖突,同時(shí)要考慮緩存的命中率,不要浪費(fèi)緩存的空間。
2.6 Controller,Interface類,接口實(shí)現(xiàn)類編寫注意事項(xiàng)
- Controller類里不能出現(xiàn)業(yè)務(wù)邏輯,只能直接調(diào)用Service類,不能直接調(diào)用Biz類(Biz類是對(duì)業(yè)務(wù)邏輯的實(shí)現(xiàn)封裝,不依賴dao層,可獨(dú)立做單元測(cè)試),manager類則是作為service、biz、dao三層的粘合劑,來(lái)協(xié)調(diào)三者執(zhí)行關(guān)系。
- Interface接口定義類只定義方法,方法的實(shí)現(xiàn)要在實(shí)現(xiàn)類里完成,interface接口以xxxService結(jié)尾,接口實(shí)現(xiàn)類以xxxServiceImpl結(jié)尾。
2.7 注釋
- 方法注釋:每個(gè)接口、方法應(yīng)該有一個(gè)方法的整體說(shuō)明,包括方法實(shí)現(xiàn)的功能、參數(shù)的詳細(xì)含義、返回值的取值及其詳細(xì)含義。
- 類注釋:對(duì)于工具類或者流程類,需要在類的注釋中對(duì)于類的功能進(jìn)行詳細(xì)的說(shuō)明;對(duì)于Model類,要對(duì)其重要的屬性添加注釋,注明其含義,必要時(shí)要重載hashCode和equals,toString方法。
3. 設(shè)計(jì)規(guī)范
3.1 對(duì)功能性模塊的抽象
業(yè)務(wù)模塊的開發(fā)過(guò)程中,應(yīng)該避免過(guò)長(zhǎng)的方法,進(jìn)行合理的功能模塊的提取以及抽象,避免同一段代碼、類似代碼到處copy的情況。
3.2 單一職責(zé)原則SRP(Single Responsibility Principle)
所謂單一職責(zé)原則,指的就是:一個(gè)類應(yīng)該僅有一個(gè)引起它變化的原因。
這里變化的原因就是 所說(shuō)的“職責(zé)”,如果一個(gè)類有多個(gè)引起它變化的原因,那么也就意味著這個(gè)類有多個(gè)職責(zé),再進(jìn)一步說(shuō),就是把多個(gè)職責(zé)耦合在一起了
。這會(huì)造成職責(zé)的相互影響,可能一個(gè)職責(zé)的變化,會(huì)影響到其它職責(zé)的實(shí)現(xiàn),甚至引起其它職責(zé)跟著變化,這種設(shè)計(jì)是很脆弱的。
這個(gè)原則看起來(lái)是最簡(jiǎn)單和最好理解的,但是實(shí)際上是很難完全做到的,難點(diǎn)在于如何區(qū)分這個(gè)“職責(zé)”
,這是個(gè)沒有標(biāo)準(zhǔn)量化的東西,哪些算職責(zé),到底這個(gè)職責(zé)有多大的粒度,這個(gè)職責(zé)如何細(xì)化等等。因此,在實(shí)際開發(fā)中,這個(gè)原則也是最容易違反的。
3.3 開放-關(guān)閉原則OCP(Open-Closed Principle)
所謂開放-關(guān)閉原則,指的就是:一個(gè)類應(yīng)該對(duì)擴(kuò)展開放,對(duì)修改關(guān)閉。一般也被簡(jiǎn)稱為開閉原則,開閉原則是設(shè)計(jì)中非常核心的一個(gè)原則。
開閉原則要求的是,類的行為是可以擴(kuò)展的
,而且是在不修改已有的代碼的情況下進(jìn)行擴(kuò)展,也不必改動(dòng)已有的源代碼或者二進(jìn)制代碼。
看起來(lái)好像是矛盾的,怎么樣才能實(shí)現(xiàn)呢?
實(shí)現(xiàn)開閉原則的關(guān)鍵就在于合理的抽象,分離出變化與不變化的部分,為變化的部分預(yù)留下可擴(kuò)展的方式
,比如:鉤子方法、或是動(dòng)態(tài)組合對(duì)象等等。
這個(gè)原則看起來(lái)也很簡(jiǎn)單,但事實(shí)上,一個(gè)系統(tǒng)要全部做到遵守開閉原則,幾乎是不可能的
,也沒這個(gè)必要。適度的抽象可以提高系統(tǒng)的靈活性,使其可擴(kuò)展、可維護(hù),但是過(guò)度的抽象,會(huì)大大增加系統(tǒng)的復(fù)雜程度
。應(yīng)該在需要改變的地方應(yīng)用開閉原則就可以了,而不用到處使用,從而陷入過(guò)度設(shè)計(jì)。
3.4 里氏替換原則LSP(Liskov Substitution Principle)
所謂里氏替換原則,指的就是:子類型必須能夠替換掉它們的父類型。這很明顯是一種多態(tài)的使用情況,它可以避免在多態(tài)的應(yīng)用中,出現(xiàn)某些隱蔽的錯(cuò)誤。
事實(shí)上,當(dāng)一個(gè)類繼承了另外一個(gè)類,那么子類就擁有了父類中可以繼承下來(lái)的屬性和操作,理論上來(lái)說(shuō),此時(shí)使用子類型去替換掉父類型,應(yīng)該不會(huì)引起原來(lái)使用父類型的程序出現(xiàn)錯(cuò)誤。
但是,很不幸的是,在某些情況下是會(huì)出現(xiàn)問(wèn)題的,比如:如果子類型覆蓋了父類型的某些方法,或者是子類型修改了父類型某些屬性的值,那么原來(lái)使用父類型的程序就可能會(huì)出現(xiàn)錯(cuò)誤
,因?yàn)樵谶\(yùn)行期間,從表面上看,它調(diào)用的是父類型的方法,需要的是父類型方法實(shí)現(xiàn)的功能,但是實(shí)際運(yùn)行調(diào)用的確是子類型覆蓋實(shí)現(xiàn)的方法,而該方法跟父類型的方法并不一樣,那就會(huì)導(dǎo)致錯(cuò)誤的產(chǎn)生
。
從另外一個(gè)角度來(lái)說(shuō),里氏替換原則是實(shí)現(xiàn)開閉的主要原則之一,開閉原則要求對(duì)擴(kuò)展開放,擴(kuò)展的一個(gè)實(shí)現(xiàn)手段就是使用繼承,而里氏替換原則是保證子類型能夠正確替換父類型,只有能正確替換,才能實(shí)現(xiàn)擴(kuò)展,否則擴(kuò)展了也會(huì)出現(xiàn)錯(cuò)誤
。
3.5 依賴倒置原則DIP(Dependence Inversion Principle)
所謂依賴倒置原則,指的就是:
要依賴于抽象,不要依賴于具體類
。要做到依賴倒置,典型的應(yīng)該做到:
高層模塊不應(yīng)該依賴于底層模塊
,二者都應(yīng)該依賴于抽象;抽象不應(yīng)該依賴于具體實(shí)現(xiàn)
,具體實(shí)現(xiàn)應(yīng)該依賴于抽象;
很多人覺得,層次化調(diào)用的時(shí)候,應(yīng)該是高層調(diào)用“底層所擁有的接口”,這是一種典型的誤解。事實(shí)上,一般高層模塊包含對(duì)業(yè)務(wù)功能的處理和業(yè)務(wù)策略選擇,應(yīng)該被重用,應(yīng)該是高層模塊去影響底層的具體實(shí)現(xiàn)。
因此,這個(gè)底層的接口,應(yīng)該是由高層提出的,然后由底層實(shí)現(xiàn)的,也就是說(shuō)底層的接口的所有權(quán)在高層模塊
,因此是一種所有權(quán)的倒置。
倒置接口所有權(quán),這就是著名的Hollywood(好萊塢)原則:不要找我們,我們會(huì)聯(lián)系你。
3.6 接口隔離原則ISP(Interface Segregation Principle)
所謂接口隔離原則,指的就是:
不應(yīng)該強(qiáng)迫客戶依賴于他們不用的方法
。
這個(gè)原則用來(lái)處理那些比較“龐大”的接口,這種接口通常會(huì)有較多的操作聲明,涉及到很多的職責(zé)。客戶在使用這樣的接口的時(shí)候,通常會(huì)有很多它不需要的方法,這些方法對(duì)于客戶來(lái)講,就是一種接口污染,相當(dāng)于強(qiáng)迫用戶在一大堆“垃圾方法”里面去尋找他需要的方法。
因此,這樣的接口應(yīng)該被分離,應(yīng)該按照不同的客戶需要來(lái)分離成為針對(duì)客戶的接口,這樣的接口里面,只包含客戶需要的操作聲明,這樣既方便了客戶的使用,也可以避免因誤用接口而導(dǎo)致的錯(cuò)誤。
分離接口的方式,除了直接進(jìn)行代碼分離之外,還可以使用委托來(lái)分離接口,在能夠支持多重繼承的語(yǔ)言里面,還可以采用多重繼承的方式進(jìn)行分離。
3.7 最少知識(shí)原則(Least Knowledge Principle)
所謂最少知識(shí)原則,指的就是:
只和你的朋友談話
。
這個(gè)原則用來(lái)指導(dǎo)我們?cè)谠O(shè)計(jì)系統(tǒng)的時(shí)候,應(yīng)該盡量減少對(duì)象之間的交互,對(duì)象只和自己的朋友談話,也就是只和自己的朋友交互,從而松散類之間的耦合
。通過(guò)松散類之間的耦合來(lái)降低類之間的相互依賴,這樣在修改系統(tǒng)的某一個(gè)部分時(shí)候,就不會(huì)影響其它的部分,從而使得系統(tǒng)具有更好的可維護(hù)性。
那么究竟哪些對(duì)象才能被當(dāng)作朋友呢?最少知識(shí)原則提供了一些指導(dǎo):
當(dāng)前對(duì)象本身;
通過(guò)方法的參數(shù)傳遞進(jìn)來(lái)的對(duì)象;
當(dāng)前對(duì)象所創(chuàng)建的對(duì)象;
當(dāng)前對(duì)象的實(shí)例變量所引用的對(duì)象;
方法內(nèi)所創(chuàng)建或?qū)嵗膶?duì)象;
總之,最少知識(shí)原則要求我們的方法調(diào)用,必須保持在一定的界限范圍之內(nèi),盡量減少對(duì)象的依賴關(guān)系。
3.8 其它原則
除了上面提到的這些原則,還有一些大家都熟知的原則,比如:
面向接口編程;
優(yōu)先使用組合/聚合,而非繼承;
當(dāng)然也還有很多不是很熟悉的原則,比如:
一個(gè)類需要的數(shù)據(jù)應(yīng)該隱藏在類的內(nèi)部;
類之間應(yīng)該零耦合,或者只有傳導(dǎo)耦合,換句話說(shuō),類之間要么沒有關(guān)系,要么只使用另一個(gè)類的接口提供的操作;
在水平方向上盡可能統(tǒng)一的分布系統(tǒng)功能;
4. 日志規(guī)范
根據(jù)日志的嚴(yán)重程度和功能所劃分成以下五種:
FETA:致命的錯(cuò)誤,程序不能或不應(yīng)該正常運(yùn)行。FETAL級(jí)別的日志主要用于記錄非常嚴(yán)重而導(dǎo)致程序不能正常運(yùn)行的錯(cuò)誤,比如得不到FileSystem對(duì)象、Job運(yùn)行失敗、ODFS寫重要的數(shù)據(jù)文件不成功等。
ERROR:嚴(yán)重的錯(cuò)誤,但不影響程序流程,Tool可以繼續(xù)運(yùn)行。ERROR級(jí)別的日志主要用于記錄雖然嚴(yán)重但不至于影響程序運(yùn)行的錯(cuò)誤,比如Parser解析失敗等。代碼中捕獲異常后輸出的日志一部分會(huì)屬于ERROR級(jí)別。
WARN: 警告日志,不影響程序流程,Tool可以繼續(xù)運(yùn)行。WARN級(jí)別的日志與ERROR級(jí)別的日志有一些相似,但通常是不能準(zhǔn)確判斷是否出現(xiàn)問(wèn)題,需要人來(lái)判 斷,比如某輪待抓取的數(shù)據(jù)為0;而ERROR級(jí)別的日志通常是在程序運(yùn)行過(guò)程中就可以發(fā)現(xiàn)代碼、流程或數(shù)據(jù)等的某一方面或幾方面存在問(wèn)題。
INFO:信息日志,不影響程序流程,Tool可以繼續(xù)運(yùn)行。INFO級(jí)別的日志主要用來(lái)記錄一些需要統(tǒng)計(jì)的信息,比如抓取統(tǒng)計(jì),以及一些必要的諸如程序啟停等信息。
DEBUG:調(diào)試日志,不影響程序流程,Tool可以繼續(xù)運(yùn)行。DEBUG級(jí)別的日志主要用于記錄調(diào)試所用到的信息,在線上運(yùn)行的過(guò)程中該級(jí)別的日志不會(huì)輸出。
設(shè)計(jì)這些日志級(jí)別是為了方便的對(duì)日志進(jìn)行過(guò)濾,對(duì)于線上系統(tǒng),一般會(huì)將日志級(jí)別設(shè)置為WARN
,這是為了讓維護(hù)人員能夠根據(jù)日志迅速判斷問(wèn)題,另一方面不會(huì)因?yàn)轭l繁的寫磁盤也帶來(lái)性能問(wèn)題。而在查找具體問(wèn)題時(shí),可能要將日志級(jí)別調(diào)到DEBUG
,希望通過(guò)更多的輸出信息來(lái)定位問(wèn)題。
5. 日志格式
日志格式指的是輸出到日志文件的每一條日志所遵循的格式。為方便起見,每一條日志格式都由若干個(gè)“key=value”這樣的屬性對(duì)組成,不同的屬性對(duì)之間用制表符(即“\t”)分隔。有幾個(gè)特殊的屬性稍有區(qū)別,不采用“key=value”這樣的形式:
- 時(shí)間:時(shí)間是一條日志的第一項(xiàng)。而由于outlog會(huì)采用“yyyyMMddHHmmssSS”這樣的格式自動(dòng)記錄日志的時(shí)間,所以無(wú)需再增加“key=value”這種格式的時(shí)間。
- 級(jí)別:級(jí)別是一條日志的第二項(xiàng)。級(jí)別采用“[LEVEL]”這種形式,如“[INFO]”。
- 消息體:消息體是一條日志的最后一項(xiàng)。為更直觀,消息體也不采用“key=value”的格式,而直接輸出,如“This is a message.”。
除此之外的其他屬性均采用“key=value”的格式。這些屬性對(duì)位于級(jí)別和消息體之間,以制表符(即“\t”)分隔,無(wú)需排序。對(duì)于“key=value”中的“key”,有一些常用的值可以定義為常量,如“CLASS(類名)”、“ERROR(錯(cuò)誤類型)”等,對(duì)于非常見或特殊需求的“key”,可以在記log的時(shí)候自己定義,解析的時(shí)候注意保持一致即可。 屬性中還有一個(gè)特殊的屬性,即“ERROR(錯(cuò)誤類型)”,用于表示錯(cuò)誤的類型。ERROR和FETAL級(jí)別的日志中需要包含該屬性,其他級(jí)別的日志中無(wú)需包括。“ERROR”屬性的取值會(huì)有一個(gè)固定的范圍,包括但不限于以下幾種:
- IOException
- ConnectionRefused