Extract Class (提煉類)

Summary: 

某個類作了應該由兩個類作的事。創建一個新類,將相關字段和函數從舊類搬移到新類。 java

Motivation:

咱們都知道,一個類應該是一個清楚的抽象,處理一些明確的責任,可是在實際工做中,類會不斷成長擴展,隨着責任不斷增長,這個類會變得過度複雜。若是某些數據和某些函數老是一塊兒出現,某些數據常常同時變化甚至彼此相依,這就表示應該將它們分離出去。一個有用的測試就是問本身,若是搬移了某些字段和函數,會發生什麼事?其餘字段和函數是否所以變得無心義? 程序員

另外一個每每在開發後期出現的信號是類的子類化方式。若是你發現子類化隻影響類的部分特性,或若是你發現某些特性須要以一種方式來子類化,某些特性則須要以另外一種方式子類化,這就意味着你須要分解原來的類。 併發

Mechanics:

1.決定如何分解類所負的責任。 函數

2.創建一個新類,用以表現從舊類中分離出來的責任。 測試

若是舊類剩下的責任與舊類名不符,爲舊類改名 spa

3.創建「從舊類訪問新類」的連接關係 code

有可能須要一個雙向連接。可是在真正須要它以前,不要創建「重新類通往舊類」的連接 對象

4.對於想搬移的沒一個字段,運用Move Field 搬移之。 接口

5.每次搬移後,編譯、測試。 事務

6. 使用Move Method將必要函數搬移到新類。先搬移較低層函數(「被其餘函數調用」多於「調用其餘函數」者),再搬移較高層函數。

7.每次搬移以後,編譯、測試

8. 檢查,精簡每一個類的接口

若是創建起雙向鏈接,檢查是否能夠將它改成單向鏈接。

9決定是否公開新類。若是的確須要公開它,就要決定讓它成爲引用對象仍是不可變的值對象。

範例

咱們從一個簡單的person類開始

public class Person
{
    private String name;

    private String officeAreaCode;

    private String officeNumber;

    public String getName()
    {
        return name;
    }

    public String getTelephoneNumber()
    {
        return ( "(" + officeAreaCode + ")" + officeNumber );
    }  public String getOfficeAreaCode()
    {
        return officeAreaCode;
    }

    public void setOfficeAreaCode( String arg )
    {
        officeAreaCode = arg;
    }

    public String getOfficeNumber()
    {
        return officeNumber;
    }

    public void setOfficeNumber( String arg )
    {
        officeNumber = arg;
    }

}
          在這個例子中,咱們能夠將與電話號碼相關的行爲分離到一個獨立類中。首先要定義一個TelephoneNumber類來表示「電話號碼」這個概念
public class TelephoneNumber
{

}
          而後,創建從Person到TelephoneNumber的連接:
public class Person
{
    private TelephoneNumber officeTelephone = new TelephoneNumber();
...
}

如今,咱們運用Move Field移動一個字段:

public class TelephoneNumber
{
    private String areaCode;

    String getAreaCode()
    {
        return areaCode;
    }

    void setAreaCode( String arg )
    {
        areaCode = arg;
    }
}
public class Person
{ 
    public String getTelephoneNumber()
    {
        return ( "(" + getOfficeAreaCode() + ")" + officeNumber );
    }

    public String getOfficeAreaCode()
    {
        return officeTelephone.getAreaCode();
    }

    public void setOfficeAreaCode( String arg )
    {
        officeTelephone.setAreaCode( arg );
    }

     而後咱們能夠移動其餘字段,並運用Move Method將相關函數移動到TelephoneNumber類中:

public class TelephoneNumber
{
    private String areaCode;

    private String number;

    public String getTelephnoeNumber()
    {
        return ( "(" + getAreaCode() + ")" + number );
    }

    String getAreaCode()
    {
        return areaCode;
    }

    void setAreaCode( String arg )
    {
        areaCode = arg;
    }

    String getNumber()
    {
        return number;
    }

    void setNumber( String arg )
    {
        number = arg;
    }
}
public class Person
{
    private TelephoneNumber officeTelephone = new TelephoneNumber();

    private String name;

    public String getName()
    {
        return name;
    }

    public String getTelephoneNumber()
    {
        return officeTelephone.getTelephnoeNumber();
    }

    TelephoneNumber getOfficeTelephone()
    {
        return officeTelephone;
    }

}
    下一步要作的決定是:要不要對用戶公開這個新類? 咱們能夠將Person中與電話號碼相關的函數委託至TelephoneNumber,從而徹底隱藏這個新類;也能夠直接將它對用戶公開。咱們還能夠將它公開給部分用戶。

若是選擇公開新類,就須要考慮別名帶來的危險。若是公開了TelephoneNumber,而有個用戶修改了對象中的areaCode字段值,咱們又怎麼能知道呢?並且,作出修改的可能不是直接用戶,而是用戶的用戶。

面對這個問題,咱們又下列幾種選擇。

1.容許任何對象修改TelephoneNumber對象的任何部分。這就使得TelephoneNumber對象成爲引用對象,因而咱們應該考慮使用Change Value to Reference。這種狀況下,Person應該是TelephoneNumber的訪問點。

2.不容許任何人不經過Person對象就修改TelephoneNumber對象。爲此,咱們能夠將TelephoneNumber設爲不可修改的,或爲它提供一個不可修改的接口。

3. 另外一個辦法是:先複製一個TelephoneNumber對象,而後將複製獲得的新對象傳遞給用戶。但這可能會形成必定程度的迷惑,由於人們會認爲他們能夠修改TelephoneNumber對象值。此外,若是同一個TelephoneNumber對象被傳遞給多個用戶,也可能在用戶之間形成別名問題。

Extract Class是改善併發程序的一種經常使用技術,由於它使你能夠爲提煉後的兩個類分別加鎖。若是你不須要同時鎖定兩個對象,就沒必要這樣作。

這裏也存在危險性。若是須要確保兩個對象被同時鎖定,就面臨事務問題,須要使用其餘類型的共享鎖。這是一個複雜的領域,比起通常狀況須要更繁重的機制。事務頗有實用性,可是編寫事務管理程序則超出了大多數程序員的職責範圍。

相關文章
相關標籤/搜索