Long Parameter List(過長參數列) Divergent Change(發散式變化) Shotgun Surgery(散彈式修改) Feature Envy(依戀情結) Data Clumps(數據泥團) Primitive Obsession(基本型別偏執) Switch Stat... ...
我不是個偉大的程式員;我只是個有著一些優秀習慣的好程式員而己
本人比較直接,不說虛的,直接上乾貨。
目錄
Long Parameter List(過長參數列) Divergent Change(發散式變化) Shotgun Surgery(散彈式修改) Feature Envy(依戀情結) Data Clumps(數據泥團) Primitive Obsession(基本型別偏執) Switch Statements(switch驚悚現身)Long Parameter List(過長參數列)
上一節有提過,當函數的入參過多時,可以用第三招,參數對象化,把參數封裝成對象,然後參數對象當成函數的入參,達到減少參數的作用。 除了參數對象化,還可以使用另一種方法來處理。 這種方法叫做:Replace Parameter with Method(以函數取代參數)優化思路
前提,這個參數是只被賦值一次的 1、如果有必要,將參數的計算過程提煉到一個獨立函數中。 2、將函數內有使用參數的地方替換成獨立函數。 3、每次替換後,測試。 4、全部替換完成後,最後把這個參數刪除。 eg:未優化的代碼public double getPrice() { int basePrice = _quantity * _itemPrice; int discountLevel; if (_quantity > 100) discountLevel = 2; else discountLevel = 1; double finalPrice = discountedPrice (basePrice, discountLevel); return finalPrice; } private double discountedPrice (int basePrice, int discountLevel) { if (discountLevel == 2) return basePrice * 0.1; else return basePrice * 0.05; }優化 1,2,3步驟 優化參數basePrice
public double getPrice() { int basePrice = getBasePrice(); int discountLevel; if (_quantity > 100) discountLevel = 2; else discountLevel = 1; double finalPrice = discountedPrice ( discountLevel); return finalPrice; } private double discountedPrice ( int discountLevel) { if (discountLevel == 2) return getBasePrice() * 0.1; else return getBasePrice() * 0.05; } private int getBasePrice(){ return _quantity * _itemPrice; }優化4步驟 去掉參數basePrice
public double getPrice() { int discountLevel; if (_quantity > 100) discountLevel = 2; else discountLevel = 1; double finalPrice = discountedPrice ( discountLevel); return finalPrice; } private double discountedPrice ( int discountLevel) { if (discountLevel == 2) return getBasePrice() * 0.1; else return getBasePrice() * 0.05; } private int getBasePrice(){ return _quantity * _itemPrice; }優化1,2,3,4步驟,去掉discountLevel參數,獨立函數返回值要為discountLevel 最後賦值的值
public double getPrice() { double finalPrice = discountedPrice (); return finalPrice; } private double discountedPrice () { if (getDiscountLevel() == 2) return getBasePrice() * 0.1; else return getBasePrice() * 0.05; } private double getDiscountLevel(){ if (_quantity > 100) return 2; else return 1; } private double getBasePrice(){ return _quantity * _itemPrice; }從上述代碼可看出,getPrice主函數finalPrice參數已經可以直接優化了。
public double getPrice() { return discountedPrice (); }可以發現getPrice函數直接調用discountedPrice 函數,所以可用Inline Method(將函數內聯化) 合併這兩個函數
public double getPrice() { if (getDiscountLevel() == 2) return getBasePrice() * 0.1; else return getBasePrice() * 0.05; } private double getDiscountLevel(){ if (_quantity > 100) return 2; else return 1; } private double getBasePrice(){ return _quantity * _itemPrice; }我們只關心主函數的計算過程,一些過程性的計算,像上述這樣,獨立函數出來。代碼邏輯會十分清晰,可讀性很好。 存在一個重要的例外。如果明顯不希望封裝的對象與主對象之間存在某種依賴關係,可以把參數數據從封裝對象中抽出來,當成函數的參數。也是合理的。 但是要註意,當參數列太多或者參數變化頻繁時,就要考慮優化了。
Divergent Change(發散式變化)
你發現你想要修改的一個函數,卻必須同時修改諸多不相關的函數,例如,當你想要添加一個新的產品類型,你需要同步修改對產品查找,顯示,排序的函數。 有以上這些情況的話,就需要優化代碼了 針對某一外界 變化的所有相應修改,都只應該發生在單一class中,而這個新class內的所有內容都應該反應該外界變化。 通過提煉類的方式,找出因著某特定原因而造成的所有變化,獨立類。 問題原因: 通常,這種發散式修改是由於編程結構不合理或者“複製-粘貼式編程”。優化思路
運用提煉類拆分類的行為。 如果不同的類有相同的行為,你可以考慮通過繼承來合併類和提煉子類。 效果: 提高代碼組織結構 減少重覆代碼Shotgun Surgery(散彈式修改)
-- 註意霰彈式修改 與 發散式變化 區別 : 發散式變化是在一個類受多種變化影響, 每種變化修改的方法不同, 霰彈式修改是 一種變化引發修改多個類中的代碼;優化思路
1、代碼集中到某個類中 : 使用 Move Method(搬移函數) 和 Move Field(搬移欄位) 把所有需要修改的代碼放進同一個類中; 2、 代碼集中到新創建類中 : 沒有合適類存放代碼, 創建一個類, 使用 Inline Class(內聯化類) 方法將一系列的行為放在同一個類中; 3、造成分散式變化 : 上面的兩種操作會造成 Divergent Change(分散式變化), 使用Extract Class 處理分散式變化;Feature Envy(依戀情結)
函數對某個class的興趣高過對自己所處之 class的興趣。無數次經驗里,我們看到某個函數 為了計算某值,從另一個對象那兒調用幾乎半打的取值函數。 影響:數據和行為不在一處,修改不可控。 解決方案:讓數據和行為在一起,通過 Extract Method(提煉函數)和Move Method(搬移函數)的方法來處理,這函數到該去的地方。 例子:參考一個優秀博主提供的例子 https://blog.csdn.net/wxr0323/article/details/7884168優化思路
1、函數全部數據來自另外一個類 做法:將數據提煉到一個獨立函數中 Move method。 2、函數部分數據來自另外一個類 做法:將“部分數據”提煉到一個函數中 Move method。 3、函數的數據來自不同類 做法:將數據分類,分別提煉各自的獨立的函數,在將這些函數移到各自屬於的類中。Data Clumps(數據泥團)
數據泥團指的是經常一起出現的數據,比如每個方法的參數幾乎相同,處理方式與過長參數列的處理方式相同,用Introduce Parameter Object(引入參數對象)將參數封裝成對象。優化思路
1、觀察經常一起出現的數據; 2、通過提煉類的方法,放到屬於他們的對象中; 3、用對象來代替這些數據; 4、編譯測試。 eg:未優化代碼 例子參考一個優秀博主提供的例子 https://blog.csdn.net/geniusxi/article/details/78581542public class Car{ // 賓士 public void printBenz(String brand, String model, Integer price, double power) { printBasicInfo(brand, model, price, power); getTax(power, price); } // 寶馬 public void printBmw(String brand, String model, Integer price, double power) { printBasicInfo(brand, model, price, power); getTax(power, price); } // 提煉列印基本信息方法 private void printBasicInfo(String brand, String model, Integer price, double power) { System.out.println("品牌" + brand); System.out.println("型號:" + model); System.out.println("動力:" + power); System.out.println("價格:" + price); } // 提煉計算稅費的方法 private double getTax(double power, Integer price){ double salePrice = price; if (price > 200000) { salePrice = price * 0.98; } if (power <= 1.6) { return salePrice * 0.05; } else { return salePrice * 0.1; } } }優化1,2步驟 上面代碼方法中,我們發現方法的參數大致相同,這時候我們可以用參數對象化來處理。
public class CarEntity { private String brand; private String model; private Integer price; private Double power; public String getBrand() { return brand; } public void setBrand(String brand) { this.brand = brand; } public String getModel() { return model; } public void setModel(String model) { this.model = model; } public Integer getPrice() { return price; } public void setPrice(Integer price) { this.price = price; } public Double getPower() { return power; } public void setPower(Double power) { this.power = power; } }優化3,4步驟
// 賓士 public void printBenz(CarEntity carEntity) { printBasicInfo(carEntity); // 計算稅費 getTax(carEntity); } // 寶馬 public void printBmw(CarEntity carEntity) { printBasicInfo(carEntity); getTax(carEntity); } private void printBasicInfo(CarEntity carEntity) { System.out.println("品牌" + carEntity.getBrand()); System.out.println("型號:" + carEntity.getModel()); System.out.println("動力:" + carEntity.getPower()); System.out.println("價格:" + carEntity.getPrice()); } // 計算稅費 private double getTax(CarEntity carEntity) { // 打折後價格 double salePrice = carEntity.getPrice(); if (carEntity.getPrice() > 200000) { salePrice = carEntity.getPrice() * 0.98; } if (carEntity.getPower() <= 1.6) { return salePrice * 0.05; } else { return salePrice * 0.1; } }經過以上的優化,代碼就更加健壯了。
Primitive Obsession(基本型別偏執)
寫代碼時總喜歡用基本類型來當參數,而不喜歡用對象。當要修改需求和擴展功能時,複雜度就增加了。優化思路
1、如果你有一組應該總是被放在一起的屬性或參數,可以用提煉類的方式來處理; 2、如果你在參數列中看到多個基本型數據,可以引用參數對象; 3、如果你發現自己正從array中挑選數據,可以用對象取代數組。 優化思路1和2之前的例子說明瞭很多次,不再重覆。 優化思路3 用對象取代數組:你有一個數組(array),其中的元素各自代表不同的東西。就可以用對象來表示數組。 eg:String[] row = new String[3]; row [0] = "Liverpool"; row [1] = "15"; //對象取代數組 Performance row = new Performance(); row.setName("Liverpool"); row.setWins("15");Performance 對象里包含name屬性和wins屬性,且這兩個屬性被定義為private ,同時擁有get方法和set方法。
Switch Statements(switch驚悚現身)
面向對象程式的一個最明顯特征就是:少用switch (或case)語句。從本質上說, switch語句的問題在於重覆(duplication)。優化思路
這種情況我們可以引用工廠 + 策略模式。用工廠把重覆的switch提煉到一起構建成一個工廠類,策略模式把switch分支中執行的動作提煉成單獨的類。 例子參考一個優秀博主提供的例子 https://blog.csdn.net/geniusxi/article/details/78581542 更多精彩內容,請等待後續更新。 此文章也同步到了:https://blog.csdn.net/shi_hong_fei_hei/article/details/80519241***************************************************************************
作者:小虛竹
歡迎任何形式的轉載,但請務必註明出處。
限於本人水平,如果文章和代碼有表述不當之處,還請不吝賜教。