最近寫代碼的一些心得

一戈擒、不放過細節(jié)


很多的隱藏很深的bug就是代碼里面一些小細節(jié)不注意導致的券躁,比如一個變量被定義的位置,或者一個自己不了解細節(jié)的api調(diào)用等谒主。很多細節(jié)的處理也決定了代碼質(zhì)量朝扼,比如命名、代碼規(guī)范的處理等霎肯。
我們看下面這段代碼:

private void reduceValueConvert(CouponCategoryDTO dto) {
    BigDecimal reduceValue = BigDecimal.ZERO;
    if (MarketConstant.CategoryType.ZKQ.equals(dto.getType()) || MarketConstant.CategoryType.SPQ.equals(dto.getType())) {
        reduceValue = dto.getReduceValue().multiply(new BigDecimal(10)).subtract(new BigDecimal(10)).abs();
    }
    if (MarketConstant.CategoryType.SPQ.equals(dto.getType()) && MarketConstant.DiscountType.FPQ.equals(dto.getDiscountType())) {
        reduceValue = dto.getReduceValue();
    }
    dto.setReduceValue(reduceValue);
}

這是我前段時間在排查問題的時候看到的代碼擎颖,剛好這段代碼的細節(jié)問題非常多。這里先簡單介紹一下這個函數(shù)的目的:根據(jù)傳入的CouponCategoryDTO判斷優(yōu)惠券是商品券還是折扣券观游,如果是商品券MarketConstant.CategoryType.SPQ搂捧,則不做轉(zhuǎn)換,如果是MarketConstant.CategoryType.ZKQ懂缕,則將傳入的dto里面的reduceValue設(shè)置成供頁面顯示的X折這種形式允跑。邏輯非常簡單,但代碼看上去卻差點意思搪柑,我們來剖析一下看看:

命名

這個方法名叫reduceValueConvert聋丝,從字面意思上理解的話,可以理解為“轉(zhuǎn)換減少值”工碾,應該是針對傳入的dto對象中的reduceValue做轉(zhuǎn)換的弱睦。但是具體要做什么樣的轉(zhuǎn)換,以及為什么要轉(zhuǎn)換渊额,單從方法名上是看不出來的况木,那假設(shè)換一種命名方式叫做:getDisplayReduceValue,這時候理解起來就知道旬迹,ok火惊,這是因為reduceValue需要轉(zhuǎn)換成ui需要的顯示格式,所以定義一個函數(shù)專門用來干這件事情奔垦,方法名具體了很多矗晃,也更好理解了。

傳參

我們看之前這個方法傳了個CouponCategoryDTO進去宴倍,雖然說這樣傳參減少了參數(shù)的數(shù)量张症,但是一旦傳入這個對象,這就是個引用傳遞鸵贬,那么在方法內(nèi)部是可以修改這個參數(shù)內(nèi)部屬性的數(shù)據(jù)的(這個函數(shù)確實這么干了??)俗他,對外部調(diào)用來說,如果后續(xù)依賴這個參數(shù)對象的話阔逼,不建議直接傳對象進方法兆衅,而是new一個新的對象,或者只傳需要的參數(shù)進入這個方法就可以了。

簡潔

我們看到羡亩,函數(shù)內(nèi)部有2個if判斷摩疑,而后面那個if判斷會修改前面那個if代碼塊里面的reduceValue返回值,閱讀起來還是比較啰嗦的畏铆,有簡化空間雷袋;對于靜態(tài)枚舉變量,可以使用static import簡化很多代碼辞居。

BUG

這個代碼里面是隱藏一個bug的楷怒,細心就會發(fā)現(xiàn),函數(shù)里面的2個if塊瓦灶,可能都沒有滿足鸠删,這種情況下,函數(shù)會將dto里面的reduceValue設(shè)置成BigDecimal.ZERO贼陶,這里就引發(fā)了bug了刃泡。產(chǎn)生這個BUG的原因其實還是寫代碼在細節(jié)處理上的壞習慣:不要在函數(shù)內(nèi)部改變參數(shù)對象的屬性

重構(gòu)后代碼

這里我對這段代碼進行了一些重構(gòu)碉怔,先看代碼:

private BigDecimal getDisplayReduceValue(BigDecimal originalReduceValue, Integer categoryType, Integer discountType) {
    if (ZKQ.equals(categoryType) || SPQ.equals(categoryType)) {
        if (!FPQ.equals(discountType)) {
            return originalReduceValue.multiply(BigDecimal.TEN).subtract(BigDecimal.TEN).abs();
        }
    }
    return originalReduceValue;
}

我們看到烘贴,重構(gòu)后的代碼,函數(shù)名非常具體眨层,就是去獲取顯示折扣庙楚,然后傳入的3個參數(shù)名也很具體上荡,使用了static import讓代碼相對簡潔一些趴樱。只在指定的if塊里面進行轉(zhuǎn)換,其它情況仍然返回originalReduceValue酪捡。代碼沒有改變?nèi)魏螀?shù)的值叁征,具體對返回值做什么處理,由調(diào)用該函數(shù)的使用者來決定逛薇。代碼看上去也比之前更容易維護了捺疼。

二、認真學習設(shè)計原則SOLID


SOLID原則是5大設(shè)計原則的首字母簡稱永罚,分別為:

單一職責原則 SRP

在任何一個軟件模塊中啤呼,應該有且只有一個被修改的原因。這里強調(diào)2個點:
1呢袱、職責要單一官扣,其它地方動了,不應該影響我羞福,我動了惕蹄,也不應該影響別人。
2、不能有多于一個職責卖陵,因為一旦職責多了遭顶,一個改動會影響另一個。
舉個實際場景的例子泪蔫,比如我有一個類棒旗,是叫OrderService,那這里面應該都是和訂單相關(guān)的函數(shù)鸥滨,如果萬一出現(xiàn)一個支付的嗦哆、優(yōu)惠券的、購物車的婿滓,那就破壞了單一職責原則(其實這個例子不好老速,因為訂單服務職責也太多了,應該拆解為訂單查詢凸主,下單橘券,訂單支付等)卿吐。

開閉原則 OCP

軟件實體應該對擴展開放旁舰,對修改關(guān)閉嗡官。這里也強調(diào)2點:

  • 有新需求時,可以對現(xiàn)有代碼進行修改衍腥,以適應新的變化磺樱;
  • 類一旦設(shè)計完成,就可以獨立進行工作竹捉,不要再對其做任何修改。

不修改就意味著不影響現(xiàn)有業(yè)務块差,也就不會引發(fā)BUG。所以我們在設(shè)計類時倔丈,要考慮怎么樣既可以實現(xiàn)擴展功能憨闰,又不需要修改代碼。
這一點我體會很深刻需五,我現(xiàn)在公司的一個項目由于已經(jīng)上線了一年左右了,迭代了無數(shù)版本警儒,業(yè)務邏輯已經(jīng)比較復雜了眶根,大家現(xiàn)在寫代碼有點像是在修水管边琉,擰上一處閥門,往往導致另外一處地方漏水了族扰。。最后很多精力花在救火上面渔呵,導致生產(chǎn)效率越來越低砍鸠。
這里有幾個設(shè)計模式我推薦大家學習一下,可以讓代碼避免過早陷入復雜性:

  • 裝飾者模式:Wrap一個新類來擴展功能
  • 策略模式:制定一個策略接口爷辱,讓不同的策略實現(xiàn)成為可能
  • 適配器模式:不改變原有類的基礎(chǔ)上適配新功能
  • 觀察者模式:靈活添加和刪除觀察者(Listener)來擴展系統(tǒng)功能

里氏代換原則 LSP

程序中的父類都應該可以正確地被子類替換饭弓。我發(fā)現(xiàn)很多程序員寫代碼的時候,其實不太擅長使用繼承和抽象關(guān)系弟断。其實面向?qū)ο笤O(shè)計里面最偉大的概念就是抽象,理解了抽象昏翰,才能對現(xiàn)實世界的業(yè)務進行建模舍咖,才能化繁為簡锉桑,設(shè)計出簡潔的軟件架構(gòu)。
在進行抽象設(shè)計的時候民轴,程序中不應該出現(xiàn)instanceof關(guān)鍵詞,因為這種設(shè)計破壞了LSP原則瑰钮,將導致父類無法被復用(因為只有在特定子類時才生效)微驶。子類中使用的函數(shù)應該在父類中被定義浪谴,子類和父類在行為表現(xiàn)上一定要一致。

接口隔離原則 ISP

多個特定場景的接口篇恒,要好過一個寬泛的通用接口凶杖。
1、不要強迫用戶依賴那些他們并不使用的接口
2智蝠、使用多個專門的接口比使用一個總接口要好
這個比較好理解,就是我們現(xiàn)在都喜歡寫一個XXXService解虱,然后在里面定義一大堆的函數(shù)漆撞,這樣的寫法是違背ISP原則的,因為一個接口里面定義了太多的依賴函數(shù)叫挟,其實我們在其他地方調(diào)用該接口時,可能只依賴其中某幾個函數(shù)抹恳,但是卻要引入一個很大的依賴關(guān)系,如果修改了其中某個東西健霹,很容易導致其他地方出錯瓶蚂。所以盡量分成多個接口來開發(fā)。比如查詢窃这、修改分成2個接口來開發(fā)。

依賴倒轉(zhuǎn)原則 DIP

模塊之間交互應該依賴于抽象祟敛,而不是具體實現(xiàn)兆解。
1、大家依賴的是一個約定锅睛,而不關(guān)心具體實現(xiàn)細節(jié)
2历谍、領(lǐng)域?qū)硬粦撘蕾嚮A(chǔ)設(shè)施層
這2點非常重要辣垒,我們在寫代碼時,經(jīng)常會涉及到調(diào)用第三方模塊甜无,比如我訂單服務可能會查詢商品服務哥遮,那么我們之間應該首先建立好一個抽象接口約定,當我調(diào)用的時候眠饮,我只需要調(diào)用這個抽象接口就可以了,不需要關(guān)心其他服務是用什么框架寨蹋、甚至編程語言實現(xiàn)的扔茅。這里面Java里面的Spring框架是通過依賴注入方式來實現(xiàn),還有類似FeignClient這種也是一個很好的方式召娜。然后領(lǐng)域?qū)樱I(yè)務邏輯層)不應該依賴基礎(chǔ)設(shè)施層(框架、中間件等)秸讹,意思也是說雅倒,我們不應該讓自己的業(yè)務代碼被框架侵入太深,否則一旦這個框架有問題不滿足需要劣欢,那我們就比較被動了殖演。

總結(jié)

最近寫代碼比較多年鸳,遇到很多問題搔确,也總結(jié)了很多經(jīng)驗灭忠,以上只是一部分座硕。
PS:上周又遇到一個重大線上事故,我自己復盤下來华匾,收獲很大,回頭抽時間寫出來跟大家分享萨西,避免踩坑旭旭。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市源梭,隨后出現(xiàn)的幾起案子稍味,更是在濱河造成了極大的恐慌,老刑警劉巖模庐,帶你破解...
    沈念sama閱讀 211,743評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件赖欣,死亡現(xiàn)場離奇詭異,居然都是意外死亡顶吮,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,296評論 3 385
  • 文/潘曉璐 我一進店門搏恤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來湃交,“玉大人,你說我怎么就攤上這事息罗〔挪祝” “怎么了绍刮?”我有些...
    開封第一講書人閱讀 157,285評論 0 348
  • 文/不壞的土叔 我叫張陵挨摸,是天一觀的道長。 經(jīng)常有香客問我膝蜈,道長熔掺,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,485評論 1 283
  • 正文 為了忘掉前任窍帝,我火速辦了婚禮诽偷,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘报慕。我一直安慰自己,他們只是感情好飞苇,可當我...
    茶點故事閱讀 65,581評論 6 386
  • 文/花漫 我一把揭開白布蜗顽。 她就那樣靜靜地躺著,像睡著了一般忿等。 火紅的嫁衣襯著肌膚如雪崔挖。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,821評論 1 290
  • 那天狸相,我揣著相機與錄音,去河邊找鬼逸尖。 笑死,一個胖子當著我的面吹牛冷溶,可吹牛的內(nèi)容都是我干的尊浓。 我是一名探鬼主播逞频,決...
    沈念sama閱讀 38,960評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼苗胀,長吁一口氣:“原來是場噩夢啊……” “哼瓦堵!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起菇用,我...
    開封第一講書人閱讀 37,719評論 0 266
  • 序言:老撾萬榮一對情侶失蹤惋鸥,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后卦绣,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,186評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡廊蜒,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,516評論 2 327
  • 正文 我和宋清朗相戀三年山叮,在試婚紗的時候發(fā)現(xiàn)自己被綠了添履。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,650評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡汰现,死狀恐怖叔壤,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情炼绘,我是刑警寧澤,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布驮捍,位于F島的核電站,受9級特大地震影響东且,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜鲁冯,卻給世界環(huán)境...
    茶點故事閱讀 39,936評論 3 313
  • 文/蒙蒙 一色查、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧跨扮,春花似錦、人聲如沸衡创。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,757評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽拔莱。三九已至隘竭,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間动看,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,991評論 1 266
  • 我被黑心中介騙來泰國打工须误, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留仇轻,地道東北人。 一個月前我還...
    沈念sama閱讀 46,370評論 2 360
  • 正文 我出身青樓祭椰,卻偏偏與公主長得像,于是被迫代替她去往敵國和親方淤。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,527評論 2 349

推薦閱讀更多精彩內(nèi)容