天天看點

教妹學Java:求求你,别再問我為什麼要 if-else 走天下了!因為。。。

CSDN 的同學們,大家好,我是二哥呀!

在某乎上看到一個接近萬贊的高贊回答,一開始看的時候我嘴角是上揚的,還會笑出豬聲,随後情緒就急轉直下,莫名心酸!題目是這樣的:

教妹學Java:求求你,别再問我為什麼要 if-else 走天下了!因為。。。
先來看一下匿名作者的回答,沒看過的同學記得以淚洗面哈。 https://www.zhihu.com/question/300975864/answer/524059880

我曾經接手過一份代碼,遇到過一個有 30 多個 if-else 嵌套 if-else 的子產品。

心裡罵罵咧咧,“誰他喵寫的這玩意!”然後翻了一遍代碼的 history。

大緻情況是這樣的:第一個程式員寫下這段代碼時,隻有 2 個 if-else;後來需求逐漸增加,先是 1 個、2 個,然後量變引起質變,于是邏輯分支快速擴張。

到這時候已經沒有人願意去重構 switch 或者設計模式了,畢竟複雜度擺在那裡,萬一崩了還得背鍋。

大概三四個程式員接手這段代碼後,就程式設計我現在這種局面了。

第一個程式員絕對沒有料到那麼簡單的邏輯在之後會變得這麼複雜,甚至在增加第一個第二個 if-else 時,也隻是很随意的加上。

是以我覺得,這鍋絕對是甲方的,讓他娘的随便改需求。這麼一想心裡就好受多了,程式設計嘛,最重要的是看得開!

于是我又追加了兩條 if-else,然後測試、送出,下班。

插一句,如果真想重構代碼,建議先看看小傅哥的《重學 Java 設計模式》,設計模式是軟體設計中常見問題的典型解決方案,它們就像能根據需求進行調整的預制藍圖, 可用于解決代碼中反複出現的設計問題,如果不懂設計模式的話,遇到這些問題就隻能抓瞎了。

設計模式,牛逼!

看完了作者的回答,二哥強忍着無名的悲傷來說兩句。

十多年的程式設計生涯裡,的确有過無數次的沖動,想要把原有的代碼重構,想要調優,最後大多數都無疾而終,尤其是随着年齡的增長,反而越來越膽小怕事,有些真的是不敢亂動,隻能忍痛讓原有的代碼更爛一些。

畢竟背鍋是大事,嘿嘿。

有時候,不能把代碼當做是藝術品,要能夠适度忍受不完美,程式能跑起來,bug 數量可控,有啥問題可以解決也是很重要的。

如果重構了,出了問題,自己背鍋是注定的,可能還會連累了測試小姐姐。

記得在外企的第二年,由于組裡面有個新人的代碼寫得實在是太爛,我就忍不住前前後後優化了一遍,畢竟作為 Team Leader,要對新人負責,要為團隊負責,結果大家猜怎麼樣?

我被上司臭罵了一頓!

原因很簡單,我特麼引入了一個 Bug,Code Review 的時候還沒有檢查出來,測試也沒有測試到,結果到了正式環境,剛巧碰到上司在日方出差,上司要給上司的上司展示成果,結果程式出了 bug,然後上司被狠狠地臭罵了一頓。

上司被批了,那自然一通越洋電話打過來,把我直接罵哭!

當時還年輕,那叫一個委屈啊。但能怎麼辦,自己的鍋不背讓誰背?

後來回洛陽後,團隊規模變小,自己重構的欲望又湧上心頭,畢竟這次沒人能管得了我,看到誰寫的代碼爛,就直接一頓操作猛如虎,重構到自己心滿意足為止。

即便是引入了新的 Bug 也沒關系,畢竟老闆也不懂,好忽悠,嘿嘿。

老闆雖然不懂代碼,但懂得寫代碼哪能沒有 Bug——經過我的不懈努力,成功給老闆灌輸了這個思想,要想不出 Bug,就增加測試團隊的人手,上司可不願意多發一份工資。

成功洗腦老闆後,我真的有一段時間是飄到了極點,狠起來連自己的代碼都重構,一遍又一遍,手頭最經常看的兩本書,一本《代碼的整潔之道》,一本《重構·改善既有代碼的設計》。

從簡單的變量命名、方法命名,到縮減方法的行數,能拆分就拆分,盡量保證每個方法的行數不超過一個小拇指那麼長。為了适配設計模式,我當時還買了一本《設計模式之禅》,真的是殚精竭慮。

現在想想那段日子真瘋狂,有時候為了修自己重構後帶來的新 Bug,真的是熬了不少夜。

但有一說一,那段日子的進步也是肉眼可見的。

不過,話又說回來,對穩定性要求比較高的項目,如果能力沒到那份上,還是盡量少重構,搞不好版本更新的日志裡就會寫下一條:XXX 程式員被祭天了!

最好是等到上司忍不住下了死指令,限爾等多少天之内,務必把這座屎山給搬走!到了那時候,再大展拳腳也不遲。

如果真的是安耐不住,一肚子的重構、調優想法無法得到施展,我給大家推薦一個好辦法,就是自己搞一個練手項目,可以是自己開發的,也可以是 GitHub 上成熟的項目,比如說我一直推薦的 vhr、mall、miaosha 等等,把源碼 fork 下然後拉下來,在本地跑一跑,嘗試去讀一讀源碼,覺得哪裡需要重構了,就動手實踐一遍,即便是出錯了,也誰都影響不到,對吧?

有些同學如果覺得自己比較厲害的話,可以去拿那些頂級的第三方類庫做實驗,重構完一定要記得測試,并且在送出 PR 的時候附帶上自己的測試報告,如果項目的作者認為你重構的有水準,沒準你一躍就成為了項目的維護者,履歷上也是加分項。

但對于公司的那堆屎山,動刀子的時候盡量猥瑣點,免得把自己埋了。

再說回 if-else 和 switch 這個題目。朋友 @yes 在回答裡提到過 Dubbo 源碼中的 ChannelEventRunnable 類的 run() 方法,我用 Sourcegraph 插件看了一下 GitHub 上 Dubbo 的源碼,還真的是挺有學習價值的。

public void run() {
    if (state == ChannelState.RECEIVED) {
        try {
            handler.received(channel, message);
        } catch (Exception e) { }
    } else {
        switch (state) {
            case CONNECTED:
                try {
                    handler.connected(channel);
                } catch (Exception e) {}
                break;
            case DISCONNECTED:
                try {
                    handler.disconnected(channel);
                } catch (Exception e) {}
                break;
            case SENT:
                try {
                    handler.sent(channel, message);
                } catch (Exception e) { }
                break;
            case CAUGHT:
                try {
                    handler.caught(channel, exception);
                } catch (Exception e) { }
                break;
            default:
        }
    }
}      

看到沒,這段代碼裡先用 if 做了判斷,然後才在 else 中使用 switch 做了分支判斷。為什麼不全部使用 switch 呢?

官方還特意給了個說明。

我把其中關鍵的一點摘錄出來,大家看一下就明白了。

現代 CPU 都支援分支預測(branch prediction)和指令流水線(instruction pipeline),這兩個結合可以極大提高 CPU 效率。對于像簡單的 if 跳轉,CPU 是可以比較好地做分支預測的。但是對于 switch 跳轉,CPU 則沒有太多的辦法。switch 本質上是根據索引,從位址數組裡取位址再跳轉的。

是以說,不是所有情況下,把 if-else 重構成 switch 就是最好的選擇,還是要因地制宜,是以求求你,别再問我為什麼要 if-else 走天下了!