一 影響代碼差的根因
1 差代碼的體現(xiàn)
我們可以列舉出非常多質(zhì)量差的代碼的表現(xiàn)現(xiàn)象,如名字不知所意、超大類(lèi)、超大方法、重復(fù)代碼、代碼難懂、代碼修改困難……其中最為影響代碼質(zhì)量的兩個(gè)表現(xiàn)是命名名不副實(shí)、邏輯可擴(kuò)展性差,當(dāng)一個(gè)新人閱讀代碼時(shí),有時(shí)發(fā)現(xiàn)方法命名與實(shí)際邏輯對(duì)不上,這就讓人感到非常疑惑,這種現(xiàn)象在平時(shí)工作并不少見(jiàn);另一個(gè)就是邏輯擴(kuò)展性差,一個(gè)新業(yè)務(wù)需求提出來(lái)后,發(fā)現(xiàn)要在多處改動(dòng),需要回歸的業(yè)務(wù)邏輯比較多,造成研發(fā)效率不高。
2 問(wèn)題歸納
對(duì)第1節(jié)中提到的現(xiàn)象進(jìn)行問(wèn)題歸納整理,大致整理出6類(lèi)問(wèn)題,分別展開(kāi)加以說(shuō)明。
3 根因分析
接下來(lái)分析下為什么會(huì)產(chǎn)生代碼差的原因,這個(gè)問(wèn)題有外部原因,也有內(nèi)部原因。外部原因主要有:項(xiàng)目排期急,沒(méi)有多少時(shí)間去設(shè)計(jì);資源短缺,人手不夠,只能怎么快怎么來(lái);緊急問(wèn)題修復(fù),臨時(shí)方案快速處理……。內(nèi)部原因主要有:自身技能低,怎么技能沒(méi)有掌握到,如Lamda表達(dá)式、常用的工具類(lèi)、框架高級(jí)用法等;無(wú)極致追求的精神,僅僅完成需求就行,穩(wěn)定性、可擴(kuò)展性、性能、數(shù)據(jù)一致性等沒(méi)有考慮……
筆者認(rèn)為最為關(guān)鍵的是內(nèi)部自身的問(wèn)題,根因就兩個(gè):自我要求不高;無(wú)反饋通道。如果對(duì)自已要求不高,僅僅滿(mǎn)足完成需求開(kāi)發(fā)就止步了,很難寫(xiě)出高質(zhì)量的代碼,另外如果沒(méi)有外部反饋,也難以提高自己的技能。筆者之前的主管非常嚴(yán)厲,對(duì)大家寫(xiě)的代碼review比較仔細(xì),一個(gè)變量名、一段邏輯的寫(xiě)法,反復(fù)讓修改,這其實(shí)是提升技能最快的方法。
二 提升代碼質(zhì)量的方法
提升代碼質(zhì)量的方法,筆者喜歡用三個(gè)方法:領(lǐng)域建模、設(shè)計(jì)原則、設(shè)計(jì)模式,主要談下如何使用。
三 領(lǐng)域模型的作用
領(lǐng)域建模的入門(mén)門(mén)檻比較高,包含了一些難理解的概念。本篇文章中并不會(huì)講述如何進(jìn)行建模(可以私下交流),筆者發(fā)現(xiàn)讓大家接受領(lǐng)域建模遠(yuǎn)比知道如何建模更重要,當(dāng)你知道了領(lǐng)域建模的作用后,自己會(huì)想各種辦法去學(xué)習(xí)。下面通過(guò)筆者經(jīng)歷的一些實(shí)際案例進(jìn)行闡述,讓大家聽(tīng)起來(lái)并不感覺(jué)到那么空洞。
1 簡(jiǎn)化認(rèn)識(shí)
筆者工作一年后加入到了一家金融公司,當(dāng)時(shí)對(duì)金融一無(wú)所知,開(kāi)始接觸到標(biāo)的、債權(quán)、債權(quán)轉(zhuǎn)讓、融資擔(dān)保、非融資擔(dān)保等名詞后,一時(shí)感到無(wú)所適從,每天要學(xué)習(xí)非常多的新內(nèi)容。
兩個(gè)月后,我的主管給我們做了一次分享,就拿了一張ppt來(lái)講,它里面包含了領(lǐng)域的實(shí)體,以及實(shí)體之間的關(guān)聯(lián)關(guān)系,一下子我就知道了整個(gè)業(yè)務(wù)是怎么玩轉(zhuǎn)的。模型的作用就是簡(jiǎn)化人對(duì)事物的認(rèn)識(shí),如果一開(kāi)始我們就陷入到代碼細(xì)節(jié)中,很難看到業(yè)務(wù)的全貌,而且代碼是為了實(shí)現(xiàn)業(yè)務(wù)能力,當(dāng)你知道了業(yè)務(wù)之后,再去看代碼就會(huì)快得多。
2 統(tǒng)一認(rèn)識(shí)
在公司里,有研發(fā)、產(chǎn)品、運(yùn)營(yíng)、測(cè)試……,當(dāng)我們?cè)谝黄鸾涣鞯臅r(shí)候,大家默認(rèn)的語(yǔ)言是不統(tǒng)一的,開(kāi)發(fā)經(jīng)常講怎么操作這張數(shù)據(jù)庫(kù)表,產(chǎn)品經(jīng)常講業(yè)務(wù)模式……這就導(dǎo)致大家的認(rèn)識(shí)并不統(tǒng)一。
那是一個(gè)晚上,剛和交互同學(xué)確認(rèn)完交互流程后,突然她問(wèn)了一個(gè)問(wèn)題:把相似的頁(yè)面讓賣(mài)家移到同一個(gè)文夾中,這個(gè)好實(shí)現(xiàn)吧?聽(tīng)完后告知不能,交互同學(xué)一聽(tīng)說(shuō)這很合理呀,怎么實(shí)現(xiàn)不了?開(kāi)始給她講了下現(xiàn)有的系統(tǒng)流程,發(fā)現(xiàn)她聽(tīng)得一臉懵逼,馬上發(fā)現(xiàn)問(wèn)題了,我是用開(kāi)發(fā)的語(yǔ)言在描述問(wèn)題,立馬換了一種方式,找了一支筆和一張紙,給交互同學(xué)畫(huà)了我們的領(lǐng)域模型是什么,業(yè)務(wù)實(shí)體之間的交互是怎樣的,一講完后,交互同學(xué)馬上明白了為什么不能實(shí)現(xiàn)的原因所在了。
3 指導(dǎo)設(shè)計(jì)
有的同學(xué)覺(jué)得領(lǐng)域建模偏空洞,比較虛,其實(shí)除了能夠簡(jiǎn)化認(rèn)識(shí)和統(tǒng)一認(rèn)識(shí)外,領(lǐng)域建模還可能指導(dǎo)代碼設(shè)計(jì),比如上面舉的店鋪導(dǎo)航Tab的例子,筆者就是通過(guò)領(lǐng)域建模來(lái)設(shè)計(jì)的,雖然它是一個(gè)小的需求,并不妨礙領(lǐng)域建模的運(yùn)用。在下圖中,可以清晰的看到,導(dǎo)航欄包含了若干個(gè)Tab,一個(gè)Tab包含規(guī)格信息和點(diǎn)擊操作信息。把這個(gè)業(yè)務(wù)模式畫(huà)出來(lái)之后,對(duì)應(yīng)的代碼中也會(huì)有上面的概念,現(xiàn)實(shí)與代碼之間存在映射關(guān)系,模型即代碼,代碼即模型。如果你的模型不能反映現(xiàn)實(shí),模塊只能算是一個(gè)花架子,范鋼老師對(duì)此總結(jié)了三句話(huà):現(xiàn)實(shí)有什么事物,對(duì)應(yīng)有什么對(duì)象;現(xiàn)實(shí)事物有什么行為,對(duì)應(yīng)對(duì)象有什么方法;現(xiàn)實(shí)事物有什么聯(lián)系,對(duì)應(yīng)對(duì)象有什么關(guān)聯(lián)。
四 設(shè)計(jì)原則的底層邏輯
1 SOLID
對(duì)于設(shè)計(jì)原則,一般我們會(huì)談到SOLID,它包含了五個(gè)設(shè)計(jì)原則:
2 為什么要有設(shè)計(jì)原則
我們對(duì)SOLID原則基本上聽(tīng)說(shuō)過(guò)或者了解過(guò),但為什么要有這些設(shè)計(jì)原則呢?為了回答這個(gè)問(wèn)題,我們從目標(biāo)往下推導(dǎo)下。軟件開(kāi)發(fā)的目標(biāo)是高內(nèi)聚、低耦合,這句掛在嘴邊的話(huà),發(fā)現(xiàn)很難衡量,比如要回答:什么樣的叫高內(nèi)聚?什么樣的叫低耦合?高內(nèi)聚要高到什么程度?低耦合要低到什么程度?這四個(gè)問(wèn)題并不太好回答。
反過(guò)來(lái)想想,如果我們的代碼不是高內(nèi)聚和低耦合的會(huì)怎樣?也即是低內(nèi)聚和高耦合的場(chǎng)景。如果代碼是低內(nèi)聚和高耦合,則會(huì)出現(xiàn)修改一個(gè)邏輯,會(huì)導(dǎo)致多處代碼要修改,這個(gè)并不是我們希望看到的,尤其在修改原有的邏輯,很容易出現(xiàn)bug,比如筆者之前修改一個(gè)問(wèn)題,改了另外一處的規(guī)則,看起來(lái)是沒(méi)有問(wèn)題,結(jié)果影響到了一個(gè)業(yè)務(wù)方,這也是為什么開(kāi)閉原則提出對(duì)修改關(guān)閉的原因,修改原有的邏輯是有風(fēng)險(xiǎn)的。
理想的情況是修改只限定在某個(gè)局部范圍內(nèi),這樣影響的范圍有限,因此我們要求邏輯要單一,不要包含多個(gè)職責(zé)。再往下思考下:為什么我們要修改呢?除了原有邏輯有bug要修復(fù)、代碼重構(gòu)外,一個(gè)重要的原因是需求發(fā)生了變化,是變化導(dǎo)致我們要對(duì)原有的邏輯進(jìn)行修改。如果沒(méi)有修改的場(chǎng)景,也就沒(méi)有所謂的高內(nèi)聚、低耦合之說(shuō)了。因此設(shè)計(jì)原則的底層邏輯就是讓軟件能夠較好地應(yīng)對(duì)變化,降本增效。
3 如何落地實(shí)踐
設(shè)計(jì)原則只是一個(gè)指導(dǎo)的方針,離落地實(shí)踐還有很大的一段距離,就像有些同學(xué)說(shuō)設(shè)計(jì)原則我懂了,但我依然運(yùn)用不到。實(shí)際上這個(gè)問(wèn)題的本質(zhì)還是對(duì)設(shè)計(jì)原則的底層邏輯沒(méi)有理解,沒(méi)有洞察出變化關(guān)注點(diǎn),怎么解決這個(gè)問(wèn)題呢?設(shè)計(jì)模式給出的答案:找到變化、封裝變化。
五 設(shè)計(jì)模式的本質(zhì)
設(shè)計(jì)模式請(qǐng)參考筆者之前寫(xiě)的文章。
六 案例實(shí)踐
當(dāng)調(diào)用的接口有不同的實(shí)現(xiàn)時(shí)(入?yún)ⅰ⒊鰠ⅰ⒔涌诙疾幌嗤枰橄蟪鲆粚臃栏瘜樱趺慈?shí)現(xiàn)呢?接下來(lái)分別看2個(gè)案例,這2個(gè)案例的側(cè)重點(diǎn)不一樣,一個(gè)是偏行為的抽象,一個(gè)是偏結(jié)構(gòu)的抽象。
1 店鋪品牌查詢(xún)
店鋪需要查詢(xún)店鋪品牌信息,然而Lazada和AE的接口是不一樣的,怎么抽象防腐層呢?
首先最簡(jiǎn)單的方案很容易想到,就是定義一個(gè)接口,然后有兩個(gè)實(shí)現(xiàn)。它的優(yōu)點(diǎn)是層次簡(jiǎn)單,大家基本看了就懂。它的缺點(diǎn)也是明顯的,在兩個(gè)實(shí)現(xiàn)類(lèi)中,職責(zé)不一單一,承擔(dān)了兩個(gè)職責(zé):一個(gè)是實(shí)現(xiàn)店鋪品牌的查詢(xún),另一個(gè)是數(shù)據(jù)轉(zhuǎn)換。
根據(jù)方案一提到的缺點(diǎn),很容易想到使用適配器模式,將之前的類(lèi)拆成兩個(gè)類(lèi):一個(gè)類(lèi)是調(diào)用對(duì)應(yīng)的品牌服務(wù);另一個(gè)類(lèi)做數(shù)據(jù)適配轉(zhuǎn)換。不過(guò)此時(shí)的方式還有一個(gè)缺點(diǎn)就是在國(guó)際化場(chǎng)景下,要考慮多租戶(hù)之間的隔離,比如Lazada有多個(gè)站點(diǎn),如何實(shí)現(xiàn)更細(xì)粒度的差異呢?方案三基于這些的思考就產(chǎn)生了。
方案三是引入了多租戶(hù)框架,能夠支撐多租戶(hù)場(chǎng)景。
2 店鋪優(yōu)惠券查詢(xún)
有一種"萬(wàn)金油"式開(kāi)發(fā)模式:組裝參數(shù)、調(diào)用接口、解析響應(yīng)結(jié)果,你會(huì)發(fā)現(xiàn)這種模式太萬(wàn)能了,適合所有的場(chǎng)景,這樣的開(kāi)發(fā)模式也即是"事務(wù)腳本模式"或者"面條型代碼"。
優(yōu)惠券查詢(xún)的案例,用領(lǐng)域建模的模式,首先思考有哪些實(shí)體。優(yōu)惠券查詢(xún)的本質(zhì):通過(guò)xx條件查詢(xún)返回滿(mǎn)足條件的優(yōu)惠券集合。對(duì)于優(yōu)惠券來(lái)講,有兩類(lèi)信息至關(guān)重要。一個(gè)是優(yōu)惠券的規(guī)格信息,如優(yōu)惠券名稱(chēng)、優(yōu)惠金額、有效期等;另一個(gè)是優(yōu)惠券的限制條件。在查詢(xún)的時(shí)候,是查店鋪優(yōu)惠券,還是查粉絲優(yōu)惠券,或者是查詢(xún)商品優(yōu)惠券……。因此分開(kāi)兩部分抽象優(yōu)惠券:一個(gè)是優(yōu)惠券查詢(xún)請(qǐng)求;另一個(gè)是優(yōu)惠券規(guī)格實(shí)體。
如果按照這樣的設(shè)計(jì),有一個(gè)缺點(diǎn)是業(yè)務(wù)方理解復(fù)雜度會(huì)上升,它是偏底層實(shí)現(xiàn),沒(méi)有做到使用簡(jiǎn)單。優(yōu)惠券偏產(chǎn)品交付而非僅僅功能交付。因此在底層實(shí)現(xiàn)之上,再抽象出產(chǎn)品組件,這樣業(yè)務(wù)方使用起來(lái)就比較簡(jiǎn)單。
作者 | 不拔
原文鏈接:http://click.aliyun.com/m/1000290565/
本文為阿里云原創(chuàng)內(nèi)容,未經(jīng)允許不得轉(zhuǎn)載。