利用多型重構為帶參方法

weixin_33766168發表於2017-11-08

《重構之美》之二

logo_white我在閱讀遺留程式碼時,經常發現存在這樣一種情形。在一個類中存在兩個方法,它們做了相似的工作,區別僅在於方法內部某些物件的型別。例如: 
public class WorkSheet{ 
    public void fillHeader() { 
        Header header = createHeader(); 
        for (String title:header.getTitles()) { 
            fillCell(title); 
        } 
    } 
    public void fillBody() { 
        CellGroup cellGroup = createCellGroup(); 
        for (Cell cell:cellGroup.getCells()) { 
            fillCell(cell.getText()); 
        } 
    } 
}

方法fillHeader()和fillBody()的目的都是從物件中獲得字串陣列,然後將其填充到單元格中。區別在於,獲得字串陣列的物件並不相同。前者為Header物件,後者為CellGroup物件。我們可以為其提供一個抽象的介面,以實現程式碼的有效重用: 
public interface TextDataSource { 
    public String[] getTextArea(); 
}

然後,讓Header和CellGroup類均實現該介面。由於CellGroup並沒有直接定義返回字串陣列的方法,而是通過返回的Cell物件獲得text值,因此,需要將這部分實現封裝到getTextArea()方法中: 
public class Header implements TextDataSource { 
    @Override 
    public String[] getTextArea() { 
        //這裡的實現即為原有的getTitles()實現 
        //可以保留原有的方法,並在本方法中指向該方法 
        //也可以利用Rename Method手法,直接更名該方法 
    } 

public class CellGroup implements TextDataSource { 
    @Override 
    public String[] getTextArea() { 
        List<String> textArea = new ArrayList<String>(); 
        for (Cell cell:this.getCells()) { 
            textArea.add(cell.getText()); 
        } 
        return textArea.toArray(); 
    } 
}

現在,就可以重構原有的WorkSheet類了。 
public class WorkSheet{ 
    public void fillSheet(TextDataSource dataSource) { 
        for (String text:dataSource.getTextArea()) { 
            fillCell(text); 
        } 
    } 
}

具體需要填充什麼內容,可以在呼叫fillSheet()方法時,根據傳入的引數物件來決定。經過重構之後,WorkSheet類中的重複程式碼得到了移除,且具有了更好的擴充套件性。

這一重構手法與Parameterize Method要解決的壞味道相似,同樣對相似方法提取了共同的引數,但實現的本質完全不同。它利用了多型的原理,通過對抽象方法體中的相似物件,抹去了不同型別物件之間的差異性,使得方法體中的相似結構能夠被抽取出來。

我將這一重構手法命名為Parameterize Method by Polymorphism。讓我仿照Martin Fowler的風格,給出這一重構方式的作法(Mechanics): 
1)新建一個介面,並使原有方法中的差異物件實現該介面。 
2)如果原有物件的方法與該介面定義的方法簽名不同,則運用Rename Method。 
3)編譯。 
4)新建一個引數為新介面型別的方法,使它可以替換先前所有的重複方法。 
5)編譯。 
6)將對舊方法的呼叫替換為對新函式的呼叫。 
7)編譯,測試。 
8)對所有舊方法重複上述步驟,每次替換後,修改並測試

如果在呼叫新方法時,發現建立引數實參物件是一件麻煩事,可以考慮在原有類中引入一個建立新介面物件的工廠方法,從而對複雜的建立邏輯進行封裝。







本文轉自wayfarer51CTO部落格,原文連結:http://blog.51cto.com/wayfarer/440205,如需轉載請自行聯絡原作者

相關文章