代碼越寫越多,從一開始的能跑通就行,到如今對本身的要求愈來愈高,不只要留下好的註釋,對於函數和變量的命名也要合乎其名,慢慢融入學到的設計模式,還有優化邏輯來提升性能。前端
因此看完這本《代碼整潔之道》,發現獲益良多,將書中最後一章的啓示貼一下,用來提醒本身之後編碼的要求~java
這個問題應該是存在於之前的JSP開發,如今通常都是先後端分離,後端代碼中,基本沒有前端代碼的存在了算法
遵循「最小驚訝原則」(The Principle of Least Surprise),函數或類應該實現有理由期待的行爲。後端
追索每種邊界條件,並編寫測試。要求全部代碼都有單元測試,這個條件比較嚴格,主要要注意的應該是在生產代碼中,注意業務邊界,避免NPE,數組越界等錯誤。設計模式
這裏提到安全不只僅是邊界和單元測試,還有無限循環和線程安全等意識。數組
這個就是常說的膠水代碼,這裏copy一份到另外一個地方。每次看到重複代碼,都表明遺漏了抽象。安全
能夠將重複的代碼抽象到上一層,或者封裝成公共方法,不少地方使用到的,能夠當成一個工具類的靜態方法等等。bash
學習到的設計模式,責任鏈、模板、抽象工廠等等,均可以用來消除重複代碼~前後端分離
建立抽象類來容納較高層級概念,建立派生類來容納較低層次概念。socket
同時要注意,較低層級和較高層級概念不該該混雜在一塊兒,分離要完整。
將概念分解到基類和派生類的最廣泛的緣由是較高層級概念能夠不依賴於低層級派生類概念。(也就是說,基類應該對派生類一無所知,不提到派生類的名稱)。
設計良好的模塊,讓接口保持簡單。
要學會限制類或模板中暴露的接口數量,減小類中的方法,減小函數的變量數目。(固然若是業務複雜時,簡單變量可能越加越多,這個時候就該創建一個新的參數類來封裝參數)。
儘可能保持接口緊湊,經過限制信息來控制耦合度。
有些方法可能在代碼修改後就不會再調用了,這個時候路徑就不會到達到,如今IDE很方便能看到方法是否被調用到。看到這些永遠不會運行的代碼,能夠考慮將其刪除~
變量和函數應該靠近被使用的地方定義。
本地變量應該正好在其首次被使用的位置是哪一個命名,垂直距離要短。
私有函數應該恰好在其被使用的位置下面定義。
從一而終。當心選擇約定,一旦選中,就當心持續遵循。
例如參數命名,若是選中了HttpServletRequest request,那麼以後的參數命名都按request來統一,讓代碼更加易於閱讀和修改。
不互相依賴的東西不應耦合。
不要將兩個沒有直接目的之間的模塊耦合,例如將變量、常量或函數放在不恰當的位置,普通的enum不該在特殊類中。
類的方法只應對其所屬類中的變量和函數感興趣,不應垂青其餘類中的變量和函數。
當方法經過其餘對象的訪問器和修改器來操做該對象內部數據,則它就依戀於該對象所屬類的範圍。(固然在業務開發中,常常會遇到條件判斷,而後修改對象的屬性值,我以爲這這種場景是正常,可是下面說的例子就要避免出現了)
Bad Example:在B類方法中,基本依賴A類的屬性
public class HourlyPayCalculator {
public Money calculateWeeklyPay(HourlyEmployee e) {
int tenthRate = e.getTenthRate().getPennins();
int tenthsWorked = e.getTenthsWorkeds()
int straightTime = Math.min(400, tenthsWorked);
int overTime = Math.max(0, tenthsWorked - straightTime);
int straightPay = straightTime * tenthRate;
int overtimePay = (int) Math.round(overTime * tenthRate * 1.5)
return new Money(straightPay + overtimePay);
}
}
複製代碼
能夠看到,上面的HourlyPayCalculator
類的計算工資方法,侵入到是鐘點工類e的做用範圍。
如今看起來,這個方法更應該下沉到HourlyEmployee類中,讓它對本身類中的屬性進行操做(雖然以前這樣寫沒感受到什麼不妥,但以後會改~)
我的理解上,應該是將複雜的選擇算子函數拆分紅多個子函數,而後經過組合調用,讓函數的意圖更加清晰。
代碼要儘量具備表達力。
之前寫Android代碼時,看到匈牙利語標記語法和聯排表示式,例如m_
/f_
等前綴,如今想一想這些前綴有些多餘,同時也要減小魔術數,畢竟寫個數字5,在沒有註釋的狀況下,除了開發者,估計其餘人也沒法理解這個5的實際含義~
一個例子你就能懂:
Matcher match = headerPattern.matcher(line);
if (match.find()) {
String key = match.group(1);
String value = match.group(2);
headers.put(key.toLowerCase(), value);
}
複製代碼
是否是感受變量會說話,key和value讓人一看就瞭解它們的含義,這種狀況下都不須要加註釋了~
如下這種應該是杜絕的:
Date newDate = date.add(5);
複製代碼
函數名add(int),沒法表達它的含義,是天數仍是月份增長5,因此這些表意不明的函數名是要杜絕的。
這裏的算法,應該是確認本身理解了這個函數是怎樣工做,在此基礎上,進行修改或者重構。
以爲這條建議應該是在業務複雜的狀況下或者須要擴展代碼邏輯狀況下實施的,在簡單業務狀況下,爲了不類膨脹的話,是可使用If/Else~(給本身偷懶找藉口(^o^)/~)
避免在代碼中硬編碼數字,能夠將常量數字寫在類最上面的靜態變量,或者抽象一個枚舉,闡釋這個數字的含義。
在if表達式中,將判斷式封裝起來(此方法會在條件簡單偷懶=-=):
例如: if (shouldBeDeleted(timer))
要好於 if (timer.hasExpired() && !timer.isRecurrent())
例如: if (buffer.shouldCompact())
要好於 if (!buffer.shouldNotCompact())
函數只作一件事有點難,能夠將一個函數拆解出幾個小函數,例如付工資函數,能夠從一個大方法拆分紅三個小方法,獲取人員、計算工資、打錢到銀行帳戶,這樣這些小方法能夠被其它方法複用。
經常有必要使用時序耦合,上一個步驟的結果交給下一個步驟處理,這個時候就不該該掩蔽它們之間的時序性。
例如:
public class MovieDiver {
Gradient gradient;
List<Spline> splines;
public void dive(String reason) {
saturateGradient();
reticulateSplines();
diveForMoog(reason);
}
···
}
複製代碼
dive()
方法的代碼沒有強調時序耦合,若是其餘開發人員調換了執行順序,這樣dive方法就沒法正常執行了。
能夠更新成下面的形式:
public void dive(String reason) {
Gradient gradient = saturateGradient();
List<Spline> splines = reticulateSplines(gradient);
diveForMoog(reason);
}
複製代碼
這樣經過建立順序隊列暴露時序耦合。
這個規則挺像封裝判斷條件,不過這條規則一樣適用於封裝變量: 例如,將出現過兩次的level + 1
封裝成一個變量
int nextLevel = level + 1;
if (nextLevel < tags.length) {
parts = new Parse(body, tags, nextLevel, offset + endTag);
body = null;
}
複製代碼
能夠理解爲,變量定義在類起始的位置,不要將共有變量定義在函數後面。
例如:
public ObjectOutputStream getOos() throws IOException {
if (oss == null) {
oos = new ObjectOutputStream(socket.getOutputStream());
}
return oos;
}
複製代碼
這個方法名只說了獲取輸出流,但沒說明若是輸出流不存在的狀況下,它將新建一個輸出流,因此這個函數名稱應該修改爲createOrReturnOos
。
例若有A、B、C三個類,下面這種行爲是要禁止的:
a.getB().getC().doSomething();
複製代碼
正確的作法應該是:
myCollaborator.doSomething();
複製代碼
讓咱們的直接協做者提供所需的所有服務~~