惊了!同事竟然在代码里“下毒”
“
寫這個(gè)文章是因?yàn)榍岸螘r(shí)間確實(shí)因?yàn)楣镜臉I(yè)務(wù)開發(fā)太忙太緊,所有開發(fā)都處在于加班趕項(xiàng)目,并且加入的新人較多造成了一系列代碼不可控的質(zhì)量問題。
圖片來(lái)自 Pexels
文章針對(duì)這段時(shí)間代碼出現(xiàn)的各種各樣的問題進(jìn)行了一個(gè)概況和整理,主要集中在代碼編碼的問題,抽象化的問題,還有就是涉及到微服務(wù)中調(diào)用和編寫接口的問題。
其實(shí)按道理來(lái)說(shuō),這些應(yīng)該屬于編程的基本功,貌似不太值得寫一篇文章,不過倒是可以通過這些基本功的出發(fā),去討論一個(gè)代碼編程系統(tǒng)構(gòu)建的一個(gè)本質(zhì),所以還是比較值得去展開。
大概先鋪墊下,會(huì)按照一個(gè)原則和建議來(lái)展開一個(gè)一個(gè)的進(jìn)行討論。
編碼的問題
避免過多的 IF 嵌套
所謂的“箭頭形”代碼基本都是因?yàn)榇罅康?IF 嵌套導(dǎo)致,一方面形成一個(gè)深深的箭頭形狀,在閱讀代碼造成縮進(jìn)夸張的語(yǔ)句塊。
更要命的是過深的嵌套層次導(dǎo)致代碼邏輯復(fù)雜度加深,當(dāng)閱讀到第 N 層嵌套時(shí)根本不清楚是什么邏輯才能進(jìn)入,嚴(yán)重降低代碼的可閱讀性和可維護(hù)性。
其實(shí)對(duì)應(yīng) IF-ELSE 過長(zhǎng)的主要原因無(wú)非就是對(duì)當(dāng)前狀態(tài)進(jìn)行檢查并決定繼續(xù)還是跳轉(zhuǎn)。
①使用衛(wèi)語(yǔ)句(Guard Clauses)提前返回,避免層層嵌套
先對(duì) IF/ELSE 的邏輯結(jié)構(gòu)進(jìn)行一些分析,我們基本上有兩種用法:
"優(yōu)先考慮滿足條件,進(jìn)行處理流程",代碼如下:
????if(user.getId()?==?10){//滿足條件,執(zhí)行}else{//不滿足條件,退出}“優(yōu)先考慮不滿足條件,讓其邏輯退出流程”,代碼如下:
????if(user.getId()?!=?10){//不滿足條件,退出}else{//滿足條件,執(zhí)行}這是兩個(gè)不同的邏輯結(jié)構(gòu),他們都可以寫出同樣的代碼邏輯,但是在第一種中,如果代碼量增大,嵌套增多,就很容易在條件中迷失了方向。
如果采用第二種方式把條件反過來(lái)寫,盡早的能把退出型邏輯及早的退出,這樣就可以把箭頭型的代碼解脫掉,如下圖:
②規(guī)劃好判斷條件和狀態(tài)模型
代碼如下圖:
如果是業(yè)務(wù)允許其實(shí)是可以將多個(gè)判斷條件進(jìn)行整合,這樣可以避免箭頭形代碼的出現(xiàn)。
但是僅僅一段 IF 條件判斷的語(yǔ)句又變得非常的臃腫一行都放不下,如果出現(xiàn)了非常復(fù)雜多狀態(tài)判斷和組合,可以使用“狀態(tài)表”,或者是狀態(tài)機(jī)等設(shè)計(jì)模式來(lái)進(jìn)行解耦。
③將 IF 中的業(yè)務(wù)細(xì)節(jié)進(jìn)行抽象成函數(shù)
將 IF 中繁瑣的業(yè)務(wù)細(xì)節(jié)抽成函數(shù),一方面可以減少又長(zhǎng)又臭的代碼,更利于屏蔽細(xì)節(jié),將不關(guān)流程的業(yè)務(wù)邏輯鎖定在一個(gè)特定的區(qū)域。
也利于進(jìn)行代碼閱讀,讓閱讀關(guān)注于業(yè)務(wù)的流程而不是業(yè)務(wù)實(shí)現(xiàn)的細(xì)節(jié),要善于應(yīng)用函數(shù)用于代碼的封裝和抽象。
謹(jǐn)慎多層循環(huán)嵌套中的操作
有的時(shí)候,確實(shí)幾層 for 循環(huán)的嵌套是業(yè)務(wù)實(shí)現(xiàn)的必須,但我們需要警惕的是經(jīng)過幾層循環(huán)的放大,最內(nèi)層循環(huán)執(zhí)行的數(shù)量是多層循環(huán)數(shù)量的乘積。
例如,這段代碼總共經(jīng)歷了 4 層的循環(huán),如果循環(huán)是 10x10x10x10,那么最終的 DB 操作是要經(jīng)歷單獨(dú)的開銷 10000 次。
第一,這 10000 次開銷如果是程序員在寫代碼已經(jīng)明確知道的開銷屬于業(yè)務(wù)必須那倒無(wú)妨,只怕程序員在寫代碼的時(shí)候還無(wú)意識(shí)到這個(gè)點(diǎn)是會(huì)被隨時(shí)放大。
第二,即使 10000 次開銷是屬于業(yè)務(wù)必須,那按照這個(gè)代碼來(lái)看,還是存在可以優(yōu)化的空間,可以在循環(huán)中將所有查詢條件都進(jìn)行拼湊,然后在進(jìn)行一定程度的批量查詢,可以較大程度降低 DB 的開銷。
不要隨意定義局部變量名
命名風(fēng)格我們可以參考阿里的《Java開發(fā)手冊(cè)》,這里主要指出來(lái)的是局部變量隨意命名的現(xiàn)象比較嚴(yán)重,大家一般都會(huì)以為局部變量只是在本方法內(nèi)使用,又不會(huì)對(duì)其他方法和其他人造成影響。
但殊不知局部變量名起得不好或隨意也對(duì)開發(fā)者本身造成困擾甚至連自己到不知道的錯(cuò)誤,以下是一個(gè)比較經(jīng)典的隨意起變量名的例子:
變量名 ma 和 map 沒有本身含義,并且他們的泛類又是一樣,很難保證不會(huì)再下面的代碼不小心使用錯(cuò)誤。
避免又臭又長(zhǎng)的類和方法
一點(diǎn)都不夸張,之前看到過一類一千多行,一個(gè)方法長(zhǎng)達(dá) 300 行,IDE 大概一頁(yè)正常來(lái)說(shuō) 30-50 行(取決屏幕大小),這個(gè)叫閱讀者怎么查看。
閱讀的時(shí)候,不斷的滾輪翻頁(yè),就算是原作者,恐怕時(shí)間一長(zhǎng)也很難駕馭這個(gè)類,就不用說(shuō)后來(lái)的維護(hù)者了。
更重要的是一個(gè)類,一個(gè)方法過長(zhǎng)時(shí),會(huì)嚴(yán)重阻礙你的擴(kuò)展和修改,方法中每一個(gè)邏輯都牽扯到很多分散的上下文,會(huì)讓修改和擴(kuò)展異常困難。
按照《重構(gòu)》所說(shuō),出現(xiàn)類過長(zhǎng)的情況很多是職責(zé)不明確,一個(gè)類存在著幾十個(gè)方法,那絕對(duì)是職責(zé)過多或職責(zé)不細(xì)分。
簡(jiǎn)單列一下針對(duì)又長(zhǎng)又臭的重構(gòu)處理:
分析需要重構(gòu)類的功能。
將職責(zé)相同的方法使用組合或集成的方式抽取為獨(dú)立的類。
分析各個(gè)方法,將重復(fù)的代碼提取為函數(shù)。
命名,對(duì)類有一個(gè)好的命名有利于對(duì)類的定位和確立職責(zé)。
Log 日志要提供明確的指向,輔助定位
Log 日志要有明確的指向性,一個(gè)可以輔助調(diào)試,一個(gè)可以記錄事件,和確立定位錯(cuò)誤。
像以下的這個(gè)例子,打印了一個(gè) log.error 日志,但這個(gè)錯(cuò)誤,就算我們事后去查看日志,只知道這里有一個(gè)錯(cuò)誤日志,但究竟是哪一個(gè)用戶日志,哪一張優(yōu)惠券的日志,無(wú)從得知,不能有助于我們直接定位錯(cuò)誤。
再看一下的日志,將返回的一個(gè) List 進(jìn)行直接打印,此處的打印并無(wú)助于保留和定位問題,只會(huì)留下無(wú)價(jià)值的信息并且讓日志變得亂糟糟。
通常,我們留下實(shí)體名字和邏輯關(guān)鍵字就足以識(shí)別一條記錄。
復(fù)雜模塊,代碼未動(dòng),大綱注釋先行
要阻止一個(gè)初級(jí)的程序員一上來(lái)就寫代碼的難度堪比阻止一饑餓的人要飽餐一頓,有多少程序員被稱之為碼農(nóng),一上來(lái)就想搬磚。
在流程和系統(tǒng)的設(shè)計(jì)上,我們有 E-R 圖和流程圖,幫我們建立模型和流程。
當(dāng)我們碰到邏輯比較復(fù)雜的類或方法,我們也需要先梳理好邏輯和流程,用注釋或偽代碼定好邏輯和流程,把整體的思路確立后,搭起一個(gè)骨架,再往里面填肉(寫代碼)。
只要流程清晰,邏輯明朗,這個(gè)時(shí)候?qū)懘a其實(shí)是最簡(jiǎn)單的事情。
功能相同盡量抽象,不要發(fā)散式修改
舉這次我們構(gòu)建訂單的一個(gè)例子,見下圖:
下單在后端使用了適配者的一個(gè)設(shè)計(jì)模式,主要是包裝同一個(gè)接口對(duì)外暴露,然后根據(jù)情況(商品的邏輯)進(jìn)行實(shí)現(xiàn)類的分離。
把邏輯統(tǒng)一并包裝成統(tǒng)一接口對(duì)外暴露這個(gè)本意是良好的,但是在這里例子中,只在意了商品邏輯的分離,而忽略了,其實(shí)邏輯,例如庫(kù)存,支付,優(yōu)惠券等邏輯其實(shí)是統(tǒng)一的,是可以被抽象的。
導(dǎo)致的結(jié)果是例如需要修改優(yōu)惠券邏輯式,需要同時(shí)進(jìn)行三次幾乎一模一樣的修改。
可以從上圖看出來(lái),過早的使用適配模式,將業(yè)務(wù)在入口處進(jìn)行分離,導(dǎo)致了后續(xù)其實(shí)相同邏輯的業(yè)務(wù)代碼也進(jìn)行了分離,本來(lái) “扣庫(kù)存” “扣優(yōu)惠券” “支付”等邏輯應(yīng)該是一樣,但也使用了三套代碼進(jìn)行維護(hù)。
微服務(wù)編碼問題
RPC 接口必須是業(yè)務(wù)職責(zé)
RPC 接口是微服務(wù)的生產(chǎn)者提供一定的能力給到消費(fèi)者進(jìn)行使用,這個(gè)時(shí)候的 RPC 接口千萬(wàn)不要定義大而全的接口。
之前就發(fā)現(xiàn)有部分同學(xué)把 RPC 接口定義成:
這樣無(wú)異于把 DAO 層直接搬到了 RPC,把整個(gè) DAO 直接進(jìn)行暴露,這樣違背了微服務(wù)的接口調(diào)用原則,RPC 接口只提供最原子的功能,限制消費(fèi)者在生產(chǎn)者定義好的業(yè)務(wù)中進(jìn)行使用。
嚴(yán)禁循環(huán)調(diào)用 RPC 接口
與項(xiàng)目?jī)?nèi)編程不同的是,每個(gè) RPC 接口的調(diào)用都會(huì)伴隨著一次的網(wǎng)絡(luò)開銷,需要需要對(duì)一個(gè)接口進(jìn)行反復(fù)請(qǐng)求,這個(gè)時(shí)候可以要求 RPC 接口的提供方另外提供一個(gè)可以批量的接口,將單次反復(fù)的請(qǐng)求變成一次請(qǐng)求,減少網(wǎng)絡(luò)開銷。
使用工具輔助清理惡性代碼
P3C 插件
在使用 Eclipse 或 idea 編程中,首推使用阿里的 P3C 插件進(jìn)行輔助,代碼規(guī)范檢查插件 P3C,是根據(jù)《阿里巴巴Java開發(fā)手冊(cè)》轉(zhuǎn)化而成的自動(dòng)化插件。
使用 Skywalking 找出惡性代碼
與 P3C 直接輔助編碼不同的是,Skywalking可以在生產(chǎn)環(huán)境中通過鏈路的跟蹤確定某一個(gè)微服務(wù)的接口性能或調(diào)動(dòng)出現(xiàn)異常。
這里不累贅介紹 Skywalking 的用處,其實(shí)鏈路跟蹤不僅僅是運(yùn)維或架構(gòu)師應(yīng)該關(guān)注的點(diǎn),普通的開發(fā)者也可以借助鏈路跟蹤去回溯自己的代碼,站在一個(gè)高的角度在生產(chǎn)環(huán)境中審視代碼在鏈路中表現(xiàn)。
善于使用鏈路跟蹤往往可以發(fā)現(xiàn)在平時(shí)編碼中被忽略的問題,例如,一次不經(jīng)意的循環(huán)調(diào)用 RPC 很容易就造成超大的調(diào)用跨度,而往往在編程中開發(fā)者是未能及時(shí)感知的。
小結(jié)
在分享的時(shí)候其實(shí)還講了抽象的原則和一些設(shè)計(jì)模式的使用,這里就不累贅的復(fù)述了。
簡(jiǎn)單的說(shuō),要寫出好的性能,可讀性高,邏輯明了的代碼,往往靠的不是一次一次的 CURD,而是平時(shí)的總結(jié)和思考。
作者:陳于喆
簡(jiǎn)介:十余年的開發(fā)和架構(gòu)經(jīng)驗(yàn),國(guó)內(nèi)較早一批微服務(wù)開發(fā)實(shí)施者。曾任職國(guó)內(nèi)互聯(lián)網(wǎng)公司網(wǎng)易和唯品會(huì)高級(jí)研發(fā)工程師,后在創(chuàng)業(yè)公司擔(dān)任技術(shù)總監(jiān)/架構(gòu)師。
編輯:陶家龍
原文首發(fā)于51CTO技術(shù)棧
-?END?-
想要加入中生代架構(gòu)群的小伙伴,請(qǐng)?zhí)砑尤汉匣锶?strong>大白的微信
申請(qǐng)備注(姓名+公司+技術(shù)方向)才能通過哦!
阿里技術(shù)精彩文章推薦
往期推薦
深度:揭秘阿里巴巴的客群畫像
多隆:從工程師到阿里巴巴合伙人
阿里技術(shù)專家楚衡:架構(gòu)制圖的工具與方法論
螞蟻集團(tuán)技術(shù)專家山丘:性能優(yōu)化常見壓測(cè)模型及優(yōu)缺點(diǎn)
阿里文娛技術(shù)專家戰(zhàn)獒: 領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)詳解之What, Why, How?
阿里專家馬飛翔:一文讀懂架構(gòu)整潔之道
阿里專家常昊:新人如何上手項(xiàng)目管理?
螞蟻集團(tuán)沈凋墨:Kubernetes-微內(nèi)核的分布式操作系統(tǒng)
阿里合伙人范禹:常掛在阿里技術(shù)人嘴邊的四句土話
阿里技術(shù)專家都鐸:一文搞懂技術(shù)債
支付寶研究員兼OceanBase總架構(gòu)師楊傳輝:我在數(shù)據(jù)庫(kù)夢(mèng)之隊(duì)的十年成長(zhǎng)路
阿里技術(shù)專家麒燁:修煉測(cè)試基本功
阿里計(jì)算平臺(tái)掌門人賈揚(yáng)清:我對(duì)人工智能方向的一點(diǎn)淺見
螞蟻資深算法專家周俊:從原理到落地,支付寶如何打造保護(hù)隱私的共享智能?
阿里高級(jí)技術(shù)專家簫逸:如何畫好一張架構(gòu)圖?
阿里高級(jí)技術(shù)專家張建飛:應(yīng)用架構(gòu)分離業(yè)務(wù)邏輯和技術(shù)細(xì)節(jié)之道
螞蟻科技 Service Mesh 落地實(shí)踐與挑戰(zhàn) | GIAC 實(shí)錄
阿里6年,我的技術(shù)蛻變之路!
螞蟻集團(tuán)涵暢:再啟程,Service Mesh 前路雖長(zhǎng),尤可期許
阿里P9專家右軍:大話軟件質(zhì)量穩(wěn)定性
阿里合伙人程立:阿里15年,我撕掉了身上兩個(gè)標(biāo)簽
阿里高工流生 | 云原生時(shí)代的 DevOps 之道
阿里高級(jí)技術(shù)專家邱小俠:微服務(wù)架構(gòu)的理論基礎(chǔ) - 康威定律
阿里P9專家右軍:以終為始的架構(gòu)設(shè)計(jì)
阿里P8架構(gòu)師:淘寶技術(shù)架構(gòu)從1.0到4.0的架構(gòu)變遷!12頁(yè)P(yáng)PT詳解
阿里技術(shù):如何畫出一張合格的技術(shù)架構(gòu)圖?
螞蟻資深技術(shù)專家王旭:開源項(xiàng)目是如何讓這個(gè)世界更安全的?
阿里資深技術(shù)專家崮德:8 個(gè)影響我職業(yè)生涯的重要技能
儒梟:我看技術(shù)人的成長(zhǎng)路徑
阿里高級(jí)技術(shù)專家宋意:平凡人在阿里十年的成長(zhǎng)之旅
阿里技術(shù)專家甘盤:淺談雙十一背后的支付寶LDC架構(gòu)和其CAP分析
阿里技術(shù)專家光錐:億級(jí)長(zhǎng)連網(wǎng)關(guān)的云原生演進(jìn)之路
阿里云原生張羽辰:服務(wù)發(fā)現(xiàn)技術(shù)選型那點(diǎn)事兒
螞蟻研究員玉伯:做一個(gè)簡(jiǎn)單自由有愛的技術(shù)人
阿里高級(jí)技術(shù)專家至簡(jiǎn): Service Mesh 在超大規(guī)模場(chǎng)景下的落地挑戰(zhàn)
阿里巴巴山獵:手把手教你玩轉(zhuǎn)全鏈路監(jiān)控
阿里涉江:你真的會(huì)學(xué)習(xí)嗎?從結(jié)構(gòu)化思維說(shuō)起
螞蟻金服資深技術(shù)專家經(jīng)國(guó):云原生時(shí)代微服務(wù)的高可用架構(gòu)設(shè)計(jì)
深入分布式緩存之EVCache探秘開局篇(文末贈(zèng)書)
螞蟻集團(tuán)俞仁杰:金融級(jí)云原生之多活容器集群高可用建設(shè)實(shí)踐
駱俊武:編程高手是如何練成的?
美團(tuán)技術(shù):復(fù)雜環(huán)境下落地 Service Mesh 的挑戰(zhàn)與實(shí)踐
Netstars CTO 陳斌:技術(shù)管理的兩種思路
? ?END ? ?? #架構(gòu)師必備#點(diǎn)分享點(diǎn)點(diǎn)贊點(diǎn)在看 新人創(chuàng)作打卡挑戰(zhàn)賽發(fā)博客就能抽獎(jiǎng)!定制產(chǎn)品紅包拿不停!總結(jié)
以上是生活随笔為你收集整理的惊了!同事竟然在代码里“下毒”的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。