Introduce Parameter Object (引入參數對象)

Summary 某些參數老是很天然地同時出現。以一個對象取代這些參數。java

                                               

動機:數組

  你常常會看到特定的一組參數老是一塊兒被傳遞。可能有好幾個函數都使用這一組參數,這些函數可能隸屬於同一個類,也可能隸屬於不一樣的類。這樣一組參數就是所謂的Data Clumps(數據泥團),咱們能夠運用給一個對象包裝全部這些數據,再以該對象取代它們。哪怕只是爲了把這些數據組織在一塊兒,這樣作也是值得的。本項重構的價值在於縮短參數列。而你知道,過長的參數列老是難以理解的。此外,新對象所定義的訪問函數還能夠是代碼更具一致性,這又進一步下降了理解和修改代碼的難度。函數

本項重構還能夠帶給你更多好處。但跟你把這些參數組織到一塊兒以後,每每很快就能夠發現一些可被移至新建類的行爲。一般本來使用那些參數的函數對這一組參數會有一些共通的處理,若是將這些共通行爲移到新對象中,你能夠減小不少重複代碼。測試

作法:spa

1.新建一個類,用以表現你想替換的一組參數。將這個類設爲不可變的。code

2.編譯。對象

3.針對使用該組參數的全部函數,實施Add Parameter,傳入上述新建類的實例對象,並將此參數值設爲nullelement

à若是你所修改的函數被其餘不少函數調用,那麼能夠保留修改前的舊函數,並令它調用修改後的新函數。你能夠先對舊函數進行重構,而後逐一修改調用端使其調用新函數,最後再將舊函數刪除。get

4.對於Data Clumps中的每一項(在此均爲參數),從函數簽名中移除之,並修改調用端和函數本體,令它們都改而經過新的參數對象取得該值。io

5.每去除一個參數,編譯並測試。

6.將原先的參數所有去除以後,觀察有無適當函數能夠運用Move Method搬移到參數對象之中。

à被搬移的多是整個函數,也多是函數中的一個段落。若是是後者,首先使用Extract Method將該段落提煉爲一個獨立函數,再搬移這一新建函數。

範例:

    下面是一個「帳目和帳項」範例。表示「帳項」的Entry實際上只是個簡單的數據容器:

class Entry...
    Entry(double value, Date chargeDate){
        _value = value;
        _chargeDate = chargeDate;
    }
    Date getDate(){
        return _chargeDate;
    }
    double getValue(){
        return _value;
    }
    private Date _chargeDate;
    private double _value;

咱們關注的焦點是用以表示「帳目」的Account,它保存了一組Entry對象,並有一個函數用來計算兩個日期間的帳項總量:

calss Account...
    double getFlowBetween(Date start, Date end){
        double result = 0;
        Enumeration e = _entries.elements();
        while(e.hasMoreElements()){
            Entry each = (Entry) e.nextElement();
            if(each.getDate.equals(start) || each.getDate().equals(end) || (each.getDate.after(            start)&& each.getDate.before(end))){
                reslut += each.getValue();
            }
        }
        return reslut;
    }
    private Vector _entries = new Vector();
    
client code ...
    double flow = anAccount.getFlowBetween(startDate, endDate);

咱們會常常看到代碼用一對值來表示一個範圍,例如表示日期範圍的start和end、表示數值範圍的upper和lower等等,咱們應該儘可能以「範圍對象」取而代之。第一個步驟是聲明一個簡單的數據容器,用以表示範圍:

class DateRange{
    DateRange(Date start, Date end){
        _start = start;
        _end = end;
    }    
    Date getStart(){
        return _start;
    }
    Date getEnd(){
        return _end;
    }
    private final Date _start;
    private final Date _end;
}

    把DateRange設爲不可變,也就是說,其中全部字段都是final,只能由構造函數來賦值,所以沒有任何函數能夠修改其中任何字段值。這是一個明智的決定,由於這樣能夠避免別名帶來的困擾。Java的函數參數都是按值傳遞的,不可變類正好可以模仿Java參數的工做方式,所以這種作法對於本項重構是最合適的。

接下來咱們把DataRange對象加到getFlowBetween()函數的參數列中:

calss Account...
    double getFlowBetween(Date start, Date end, DateRange range){
        double result = 0;
        Enumeration e = _entries.elements();
        while(e.hasMoreElements()){
            Entry each = (Entry) e.nextElement();
            if(each.getDate.equals(start) || each.getDate().equals(end) || (each.getDate.after(start)&& each.getDate.before(end))){
                reslut += each.getValue();
            }
        }
        return reslut;
    }
    private Vector _entries = new Vector();
    
client code ...
    double flow = anAccount.getFlowBetween(startDate, endDate);

至此,只須要編譯一下就好了,由於咱們還沒有修改程序的任何行爲。

下一個步驟是去除舊參數之一,以新建對象取而代之。首先刪除start參數,並修改getFlowBetween()函數及其調用者,讓它們轉而使用新對象:

calss Account...
    double getFlowBetween( Date end, DateRange range){
        double result = 0;
        Enumeration e = _entries.elements();
        while(e.hasMoreElements()){
            Entry each = (Entry) e.nextElement();
            if(each.getDate.equals(range.getStart()) || each.getDate().equals(end) || (each.getDate.after(range.getStart())&& each.getDate.before(end))){
                reslut += each.getValue();
            }
        }
        return reslut;
    }
    private Vector _entries = new Vector();
    
client code ...
    double flow = anAccount.getFlowBetween(new DateRange(startDate,null), endDate);

而後,將end參數也移除:

calss Account...
    double getFlowBetween( DateRange range){
        double result = 0;
        Enumeration e = _entries.elements();
        while(e.hasMoreElements()){
            Entry each = (Entry) e.nextElement();
            if(each.getDate.equals(range.getStart()) || each.getDate().equals(range.getEnd()) || (each.getDate.after(range.getStart())&& each.getDate.before(range.getEnd()))){
                reslut += each.getValue();
            }
        }
        return reslut;
    }
    private Vector _entries = new Vector();
    
client code ...
    double flow = anAccount.getFlowBetween(new DateRange(startDate,endDate));

如今,咱們已經引入了參數對象。咱們還能夠將適當的行爲從其餘函數移到這個新建對象中,進一步bong本項重構得到更大利益。這裏,咱們選定條件表達式中的代碼,實施Extract Method 和 Move Method,最後的到以下代碼:

calss Account...
    double getFlowBetween( DateRange range){
        double result = 0;
        Enumeration e = _entries.elements();
        while(e.hasMoreElements()){
            Entry each = (Entry) e.nextElement();
            if(range.includes(each.getDate())){
                reslut += each.getValue();
            }
        }
        return reslut;
    }
    private Vector _entries = new Vector();
    
class DateRange...
    boolean includes(Date arg){
        return arg.equals(_start) || arg.equals(_end) || (arg.after(_start)&& arg.before(_end));
    }

如此單純的提煉和搬移動做一般能夠一步完成。若是在這個過程當中出錯,能夠回到重構前的狀態,而後分紅兩個較小的步驟從新進行。

相關文章
相關標籤/搜索