最近在看concurrent包下線程池的源碼,當我看到ThreadPoolExecutor類的時候,發現了JDK源碼的一個問題。如下是ThreadPoolExecutor類的addWorker方法的代碼片斷:java
boolean workerStarted = false; boolean workerAdded = false; Worker w = null; try { w = new Worker(firstTask); final Thread t = w.thread; if (t != null) { final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { int rs = runStateOf(ctl.get()); if (rs < SHUTDOWN || (rs == SHUTDOWN && firstTask == null)) { if (t.isAlive()) // precheck that t is startable throw new IllegalThreadStateException(); workers.add(w); int s = workers.size(); if (s > largestPoolSize) largestPoolSize = s; workerAdded = true; } } finally { mainLock.unlock(); } if (workerAdded) { t.start(); workerStarted = true; } } } finally { if (! workerStarted) addWorkerFailed(w); } return workerStarted;
這段代碼的功能是徹底沒有問題的,可是若是使用衛語句,代碼的可讀性就會更高了。那麼什麼是衛語句呢?微信
歡迎關注微信公衆號:萬貓學社,每週一分享Java技術乾貨。架構
條件表達式一般有兩種表現形式,第一種形式是:全部分支都屬於正常行爲;第二種形式則是:條件表達式提供的答案中只有一種是正常行爲,其餘都是不常見的狀況。這兩類條件表達式有不一樣的用途,這一點應該經過代碼表現出來。函數
若是兩條分支都是正常行爲,就應該使用形如if...else...的條件表達式;若是某個條件極其罕見,就應該單獨檢查該條件,並在該條件爲真時馬上從函數中返回。這樣的單獨檢查經常被稱爲「衛語句」(guard clauses)。只看概念乾巴巴的,很差理解,咱們來舉兩個例子。優化
歡迎關注微信公衆號:萬貓學社,每週一分享Java技術乾貨。this
這是一個計算員工薪資的方法,其中以特殊規則處理駐外員工和退休員工的薪資。這些狀況不常有,但的確會偶爾出現。編碼
public double getSalary() { double result; if (this.isSeparated) {//駐外員工 result = this.separatedSalary(); } else { if (this.isRetired) {//退休員工 result = this.retiredSalary(); } else {//正常員工 result = this.normalSalary(); } } return result; }
這段代碼中,非正常狀況的檢查掩蓋了正常狀況的檢查,因此應該用衛語句來取代這些條件檢查,以提升程序清晰度。對於每一個檢查,放進一個衛語句。衛語句要不就從函數中返回,要不就拋出一個異常。spa
public double getSalary() { if (this.isSeparated) {//衛語句 return this.separatedSalary(); } if (this.isRetired) {//衛語句 return this.retiredSalary(); } return this.normalSalary(); }
歡迎關注微信公衆號:萬貓學社,每週一分享Java技術乾貨。線程
這是一個已知長寬高求長方體體積的方法,但有個特殊的需求:高大於0時,打印萬貓學社
。(驚不驚喜?意不意外?突不突兀?變不變態?是的,有時候咱們接到的需求就是這樣的。)代碼是這樣的:架構設計
public double getVolume(double length, double width, double height) { double result = 0.0; if (height > 0.0) { System.out.println("萬貓學社"); if (length > 0.0 && width > 0.0) { result = length * width * height; } } return result; }
仍是用衛語句替換條件檢查,可是咱們須要將相應的條件反轉過來,也就是作邏輯非運算。
public double getVolume(double length, double width, double height) { if (height <= 0.0) {//衛語句,!(height > 0) return 0.0; } System.out.println("萬貓學社"); if (length <= 0.0 || width <= 0.0) {//衛語句,!(length > 0 && width > 0) return 0.0; } return length * width * height; }
歡迎關注微信公衆號:萬貓學社,每週一分享Java技術乾貨。
衛語句的精髓是:給某一條分支以特別的重視。若是使用if...else...結構,你對if分支和else分支的重視是同等的。這樣的代碼結構傳遞給閱讀者的消息就是:各個分支有一樣的重要性。
衛語句就不一樣了,它告訴閱讀者:「這種狀況很罕見,若是它真的發生了,請作一些必要的整理工做,而後退出。」若是對方法剩餘部分再也不有興趣,固然應該馬上退出。引導代碼的閱讀者去看一個沒有用的else區段,只會妨礙他們的理解。用了衛語句之後,代碼更容易被理解,被維護。
歡迎關注微信公衆號:萬貓學社,每週一分享Java技術乾貨。
看了上面的講解,addWorker方法的代碼片斷就能夠優化爲:
boolean workerStarted = false; boolean workerAdded = false; Worker w = null; try { w = new Worker(firstTask); final Thread t = w.thread; if (t == null) {//衛語句 return workerStarted; } final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { int rs = runStateOf(ctl.get()); //衛語句 if (rs > SHUTDOWN || (rs == SHUTDOWN && firstTask != null)) { return workerStarted; } if (t.isAlive())// precheck that t is startable throw new IllegalThreadStateException(); workers.add(w); int s = workers.size(); if (s > largestPoolSize) largestPoolSize = s; workerAdded = true; } finally { mainLock.unlock(); } if (workerAdded) { t.start(); workerStarted = true; } } finally { if (!workerStarted) addWorkerFailed(w); } return workerStarted;
修改後的代碼,理解起來是否是更容易了?假如再加入新的功能,能夠更容易修改代碼。
歡迎關注微信公衆號:萬貓學社,每週一分享Java技術乾貨。
這段JDK源碼在功能上沒有任何問題,架構設計也堪稱完美,不過我認爲在可讀性上仍是能夠優化的。相似這樣的嵌套條件表達式的代碼在JDK源碼中不止這一處,多是做者當時沒有考慮到使用衛語句,或者沒有像我這樣的吹毛求疵。但願你們在本身編碼過程當中,也能夠儘可能使用衛語句取代嵌套條件表達式,以提升代碼的清晰度和可維護性。
歡迎關注微信公衆號:萬貓學社,每週一分享Java技術乾貨。
參考文獻:《重構:改善既有代碼的設計》