須要用一種精緻的態度去寫代碼,才能寫出優美而牢固的代碼。java
壞味,一般是那些人們聞起來很是不舒服且避之不及的味道。走進垃圾桶,你就能深入感覺到壞味的存在。編程
代碼壞味,是指那些閱讀和理解起來很是拗口、困難、耗費大量腦力的代碼。代碼壞味更多反映的是風格和態度問題,而與技術沒有太大的關係(也能夠理解爲匱乏編寫可讀代碼的技術能力)。技術還能夠提高,風格和態度壞掉了,整我的的作事就 Low 掉了。這就比如一我的學了不少招式,可是一個馬步都蹲不穩。函數
好的代碼應該是怎樣的 ? 簡潔、天然、清晰。 讀起來,就像感覺到一陣微香的春風,很天然地就理解了,不會感覺到阻力。工具
本文將給出若干代碼壞味,以及如何更好地編寫。
ui
單測拼寫錯誤,這得多粗心 ?他寫代碼的時候都在想些什麼 ? 真但願這樣的人趕忙轉行,編程行業沒法容忍這種態度作事的人。code
String finasName = fans.getFansNickname(); if (StringUtil.isNotBlank(finasName)) { customer = finasName; }
最忍不住衝動的就是鏈式寫法。 以下代碼所示:orm
boolean isPayCard = goods.getGoodsRichInfo().getItemModel().isPayCard(); if (response.getData() != null && CollectionUtils.isNotEmpty(response.getData().getCartList())) { // 出參轉換 cartList = response.getData().getCartList().stream().map(CartResponseBuilder::buildCartList).collect(Collectors.toList()); }
究其緣由,是這麼寫很爽。不過爽是有代價的: NPE 潛伏其中,並且報 NPE 時,還不能直觀看出是哪個有問題。好比上面的代碼,getGoodsRichInfo() 和 getItemModel() 都有多是 null 而引起 NPE 。blog
有兩種解決技巧:1. 拆解成多個單行調用並作判空(若是變量有複用更好); 2. 用 Optional 避免 NPE。
get
鏈式寫法的一個變種,就是把全部東西都扔到一行,—— 其衝動本質是同樣的。 以下代碼所示:it
List<String> orderNos = new ArrayList<>(messages.stream().map(TcOrder::getOrderNo).collect(Collectors.toSet())); orderService.queryOrder(Long.parseLong(orderInfoList.get(0).getShopId()), orderNo)
這樣的壞處是什麼呢 ?
還有這種:
long discounts = Optional.ofNullable(item).map(Item::getPrice).orElse(0L) - Optional.ofNullable(item).map(OrderItem::getItemPrice) .map(ItemPrice::getUnitPrice).orElse(0L);
明明能夠拆解爲:
Long originPrice = Optional.ofNullable(item).map(Item::getPrice).orElse(0L); Long unitPrice = Optional.ofNullable(item).map(Item::getItemPrice) .map(ItemPrice::getUnitPrice).orElse(0L); long discounts = originPrice - unitPrice
更極端的例子:
壞味是什麼?
解決方法: 把構建 refundStatus 的部分抽離出來,以下所示。這樣,這個方法就清晰不少,也很容易進行覆蓋性單測,更可靠。
人是很容易效仿的。這樣的代碼看多了,你也會忍不住來上幾行。
經常能夠看到這樣的代碼:
if (isRetail) { // buildLocalDeliveryInfoCodeForRetail 55 lines } else { //buildLocalDeliveryInfoCodeForNormal 73 lines }
壞處是什麼:主流程很容易被分支代碼衝散,變成毫無重點的代碼堆砌;若是有多個條件分支,漸漸就會演變成多重 if-else 語句;方法愈來愈長,膨脹很快。第一我的沒作好,後面的人效仿起來,很容易就變成了一堆誰也不肯意碰的爛代碼。
對於這種情形,簡單的方案是,把多個條件分支的語句,分別抽到多個子函數,凸顯主流程;更進一步,採用策略模式,將多個子函數變成多個互不影響的組件,這樣,每一個類都很短小,各司其職,須要修改時也只要改局部便可。
我拆解過一個多重 if-else 語句,限於公司代碼規定,這裏不便透露。
多重 if-else 語句,經過一個小技巧就能夠進行「降重」:對於每一個分支,編寫子函數,而後調用它。在每一個子函數裏,能夠經過 if-return 衛述句,快速返回,更容易理解。
若是有多重條件呢 ?好比:
if (orderDetail.getIsVirtual() || orderDetail.getIsVT()){ if (isStockOverSale(orderDetail.getExtra())) { return "oversale, wait confirm"; } else if (isStockDoing(orderDetail.getExtra())) { return "wait for stock confirm"; } }
壞處是什麼? 若是我又要新增不一樣維度的條件,這裏很容易就會變成三重乃至更多重 if-else 語句, 你懂的。
解決方法:能夠將變量分離出來,將多重條件打平:
boolean isVirtual = orderDetail.getIsVirtual(); boolean isVirtualTicket = orderDetail.getIsVT(); boolean isVirtualOrder = isVirtual || isVirtualTicket; boolean isStockOverSale = isStockOverSale(orderDetail.getExtra()); boolean isStockDoing = isStockDeductDoing(orderDetail.getExtra()); if (isVirtualOrder && isStockOverSale) { return "oversale, wait confirm"; } if (isVirtualOrder && isStockDeductDoing) { return "wait for stock confirm"; }
代碼壞味不少,難以一一列舉,但其本質的特色,就是喜歡將大量邏輯不分層次地堆砌到一塊兒。解決這些代碼壞味的技巧其實很簡單:拆解子函數,調用它。 如何將代碼拆解成不一樣層次的優雅組合,這是一門技藝。
編程,是邏輯與表達並重的活動。我的認爲,重邏輯而不重表達,重技術而不重細節,是國內難以批量生產高質量軟件的重因之一。要作出高質量軟件,從寫好每一行代碼入手,而不能期望工程上有什麼銀彈可讓一堆爛代碼成就一個好軟件。須要用一種精緻的態度去寫代碼,才能寫出優美而牢固的代碼。
對於我的來講,不容忍代碼壞味,孜孜不倦地追求編寫簡潔、天然、清晰的代碼;對於企業來講,須要樹立標杆,有更多關於代碼質量的佈道者,培養編寫好代碼的技術氛圍。不然,代碼就只是賺錢養家的工具,過幾年就要所有扔掉,沒有留下什麼值得借鑑和複用的東西,陷入低水平重複建設的境地。