l

2016年2月26日 星期五

用Extract Subclass和Extrace Interface移除Large Class怪味道

Feb. 14 16:50~17:30

image

圖片來源在此

 

關於Large Class怪味道的說明請參考〈談談壞味道(2):Long Method & Large Class〉,今天介紹如何透過Extract Subclass與Extrace Interface來移除這個怪味道。

▼你設計一個USBDrive類別用來代表USB外接硬碟,一開始這個類別只需要負責處裡USB 2.0的裝置,後來因為USB 3.0問世,USBDrive也必須支援新的標準。你讓USBDrive接受一個USB3Controller類別來支援新的USB 3.0標準,保持原本USBDrive程式碼讓它處裡USB 2.0。

螢幕截圖 2016-02-14 17.00.59

 

因為需求改變,USBDrive變得越來越肥大,程式邏輯也越發複雜。以getSpeedInMegaBit()函數為例,如果isUSB2()則直接傳回480,如果不是USB 2.0則還需要判斷_controller是否為null,如果不是則透過_controller得到USB 3.0的速度。

USBDrive慢慢浮現出Large Class怪味道,該是重構它的時候。

***

▼套用Extract Subclass重構,新增USB3Drive子類別讓它繼承自USBDrive類別,然後把和USB 3.0相關的程式碼由USBDrive移到USB3Drive。

螢幕截圖 2016-02-14 17.10.14

 

▼讓USBDrive類別找回「初衷」,只用來代表USB 2.0。

螢幕截圖 2016-02-14 17.13.05

***

▼改完之後發現可以套用Extract Interface重構更進一步將USBDrive的界面抽離出來。

螢幕截圖 2016-02-14 17.16.51

 

▼原本的USBDrive類別改名為USB2Drive,並讓它實作USBDrive界面。螢幕截圖 2016-02-14 17.18.57

 

▼至於USB3Drive如果會使用到USB2Drive的程式碼,可以讓它還是繼承自USB2Drive(如下圖所示)。又或者可以再抽出一層AbstractUSBDrive抽像類別把共同的程式碼放在這裡,然後讓USB2Drive與USB3Drive都繼承自AbstractUSBDrive抽像類別。如果兩者沒有什麼共用程式碼,則可以直接讓USB3Drive實作USBDrive界面。

螢幕截圖 2016-02-26 21.25.56

***

友藏內心獨白:過胖對於人類與程式都不好。

3 則留言:

  1. 最後的結果讓USB3Drive繼承USB2Drive OK嗎?是否會違反Liskov Substitution Principle? drive = new USB3Drive(); drive.getSpeedInMegaBit(); //<- 這邊會有問題, 對吧?沒有controller

    回覆刪除
    回覆
    1. 謝謝你的提問,你說的對我的範例漏了考慮 USB3Driver() 建構函數,我再想想要怎麼改,謝謝。

      刪除
  2. controller作為strategy應該是ok的作法。補AbstractUSBDriver作template也可以,不過讓繼承深度加深是明顯缺點。 :-)

    回覆刪除