Summary: 類之中有一個數值類型碼,但它並不影響類的行爲。以一個新的類替換該數值類型碼。java
動機:函數
在以C爲基礎的變成語言中,類型碼或枚舉值很常見。若是帶着一個有意義的符號名,類型碼的可讀性仍是不錯的。問題在於,符號名終究是個別名,編譯器所看見的、進行類型校驗的,仍是背後那個數值。任何接受類型碼做爲參數的函數所指望的其實是一個數值,沒法強制使用符號名。這會大大下降代碼的可讀性,從而成爲bug之源。測試
若是把那樣的數值換成一個類,編譯器就能夠對這個類進行類型校驗。只要爲這個類提供工廠函數,你就額能夠始終保證只有合法的實例纔會被建立出來,並且它們都會被傳遞給正確的宿主對象。spa
可是在使用Replace Type Code with Class以前,你應該先考慮類型碼的其餘替換方式。只有當類型碼是純數據時(也就是類型碼不會在switch語句中引發行爲變化時),你才能以類來取代它。Java只能以整數做爲switch語句的判斷依據,不能使用任意類,所以在那種狀況下不可以以類替換類型碼。更重要的是:任何switch語句都應該運用Replace Conditional with Polymorphism去掉。爲了進行那樣的重構,你首先運用Replace Type Code with Subclasses或Replace 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]; }