Replace Type Code with Class (以類取代類型碼)

Summary 類之中有一個數值類型碼,但它並不影響類的行爲。以一個新的類替換該數值類型碼。java

                                               

動機:函數

  在以C爲基礎的變成語言中,類型碼或枚舉值很常見。若是帶着一個有意義的符號名,類型碼的可讀性仍是不錯的。問題在於,符號名終究是個別名,編譯器所看見的、進行類型校驗的,仍是背後那個數值。任何接受類型碼做爲參數的函數所指望的其實是一個數值,沒法強制使用符號名。這會大大下降代碼的可讀性,從而成爲bug之源。測試

若是把那樣的數值換成一個類,編譯器就能夠對這個類進行類型校驗。只要爲這個類提供工廠函數,你就額能夠始終保證只有合法的實例纔會被建立出來,並且它們都會被傳遞給正確的宿主對象。spa

可是在使用Replace Type Code with Class以前,你應該先考慮類型碼的其餘替換方式。只有當類型碼是純數據時(也就是類型碼不會在switch語句中引發行爲變化時),你才能以類來取代它。Java只能以整數做爲switch語句的判斷依據,不能使用任意類,所以在那種狀況下不可以以類替換類型碼。更重要的是:任何switch語句都應該運用Replace Conditional with Polymorphism去掉。爲了進行那樣的重構,你首先運用Replace Type Code with SubclassesReplace Type Code with State/Strategy,把類型碼處理掉。code

即便一個類型碼不會因其數值的不一樣而引發行爲上的差別,宿主類中的某些行爲仍是有可能更適合置放於類型碼中,所以你還應該留意是否有必要使用Move Method將一兩個函數搬過去。對象

作法:接口

1.爲類型碼創建一個類get

à這個類須要一個用以記錄類型碼的字段,其類型應該和類型碼相同,並應該由對應的取值函數,此外還應該用一組靜態變量保存容許被建立的實例,並以一個靜態函數根據本來的類型碼返回合適的實例。編譯器

2.修改源類實現,讓它使用上述新建的類。it

à維持原先以類型碼爲基礎的函數接口,但改變靜態字段,以新建的類產生代碼。而後修改類型碼相關函數,讓它們也重新建的類中獲取類型碼。

3.編譯,測試。

à此時,新建的類能夠對類型碼進行運行期檢查。

4.對於源類中每個使用類型碼的函數,相應創建一個函數,讓新函數使用新建的類。

à你須要創建「以新類實例爲自變量」的函數,用以替換原先「直接以類型碼爲參數」的函數。你還須要創建一個「返回新類實例」的函數,用以替換原先「直接返回類型碼」的函數。創建新函數前,你可使用Rename Method修改原函數名稱,明確指出哪些函數仍然使用舊式的類型碼,這每每是個明智之舉。

5.逐一修改源類用戶,讓它們使用新接口。

6.每修改一個用戶,編譯並測試。

à你也可能須要一次性修改多個彼此相關的函數,才能保持這些函數之間的一致性,才能順利地編譯、測試

7.刪除使用類型碼的舊接口,並刪除保存舊類型碼的靜態變量。

8.編譯,測試。

  範例:

  每一個人都擁有四種血型中的一種。咱們以Person來表示「人」,以其中的類型碼錶示「血型」:

class Person{
    public static final int O = 0;
    public static final int A = 1;
    public static final int B = 2;
    public static final int AB = 3;
    
    private int _bloodGroup;
    
    pulic Person(int bloodGroup){
        _bloodGroup = bloodGroup;
    }
    
    public void setBloodGroup(int arg){
        _bloodGroup = arg;
    }
    
    public int getBloodGroup(){
        return _bloodGroup;
    }
}

 首先,咱們創建一個新的BloodGroup類,用以表示「血型」,並在這個類實例中保存本來的類型碼數值:

class BloodGroup{
    public static final BloodGroup O = new BloodGroup(0);
    public static final BloodGroup A = new BloodGroup(1);
    public static final BloodGroup B = new BloodGroup(2);
    public static final BloodGroup AB = new BloodGroup(3);
    
    public static final BloodGroup[] _values = {O, A, B, AB};
    
    private final int _code;
    
    private BloodGroup(int code){
        _code = code;
    }
    
    public int getCode(){
        return _code;
    }
    
    public static BloodGroup code(int arg){
        return _values[arg];
    }
}

而後,把Person中的類型碼改成使用BloodGroup類:

class Person{
    public static final int O = BloodGroup.O.getCode();
    public static final int A = BloodGroup.A.getCode();
    public static final int B = BloodGroup.B.getCode();
    public static final int AB = BloodGroup.AB.getCode();
    
    private BloodGroup _bloodGroup;
    
    pulic Person(int bloodGroup){
        _bloodGroup = BloodGroup.code(bloodGroup);
    }
    
    public void setBloodGroup(int arg){
        _bloodGroup = BloodGroup.code(arg);
    }
    
    public int getBloodGroup(){
        return _bloodGroup.getCode();
    }
}

如今由於BloodGroup類而擁有了運行期檢驗能力。爲了真正從這些改變中獲利,咱們還必須修改Person的用戶,讓它們以BloodGroup對象表示類型碼,而再也不使用整數。

首先,使用Rename Method修改類型碼訪問函數的名稱,說明當前狀況:

class Person...
    public int getBloodGroupCode(){
        return _bloodGroup.getCode();
    }

而後爲Person加入一個新的取值函數,其中使用BloodGroup;

public BloodGroup getBloodGroup(){
    return _bloodGroup;
}

另外,還要創建新的構造函數和設值函數,讓它們也使用BloodGroup;

public Person (BloodGroup bloodGroup){
    _bloodGroup = bloodGroup;
}

public void setBloodGroup(BloodGroup arg){
    _bloodGroup = arg;
}

如今,繼續處理person用戶。此時應該注意,每次只處理一個用戶,這樣才能夠保持小步前進。每一個用戶須要的修改方式可能不一樣,這使得修改過程更加棘手。對Person內的靜態變量的全部引用點也須要修改。所以,下列代碼:

Person thePerson = new Person(Person.A);

就變成了

Person thePerson = new Person(BloodGroup.A);

原來調用取值函數的代碼必須改成調用BloodGroup的取值函數。所以下列代碼

thePerson.getBloodGroupCode();

變成了

thePerson.getBloodGroup.getCode();

設值函數也同樣。所以下列代碼

thePerson.setBloodGroup(Person.AB);

變成了

thePerson.setBloodGroup(BloodGroup.AB);

修改完畢Person的全部用戶以後,就能夠刪掉本來使用整數類型的那些舊的取值函數、構造函數、靜態變量和設值函數了。

還能夠將BloodGroup中使用整數類型的函數聲明爲private,由於再也沒有人會使用它們了:

class BloodGroup...
    private int getCode(){
        return _code;
    }
    private static BloodGroup code(int arg){
        return _values[arg];
    }
相關文章
相關標籤/搜索