Summary: 你有一個類型碼,它會影響類的行爲,但你沒法經過繼承手法消除它。以狀態對象取代類型碼。 java
動機:算法
本項重構和Replace Type Code with Subclasses很類似,但若是「類型碼的值在對象生命期中發生變化」或「其餘緣由使得宿主類不能被繼承」,你也可使用本重構。本重構使用State模式或Strategy模式。函數
State模式和Strategy模式很是類似,所以不管你選擇其中哪種,重構過程都是相同的。「選擇哪個模式」並不是問題關鍵所在,你只須要選擇更適合特定情境的模式就好了。若是你打算在本項重構完成以後再以Replace Conditional with Polymorphism簡化一個算法,那麼選擇Strategy模式比較合適;若是你打算搬移與狀態相關的數據,並且你把新建對象視爲一種變遷狀態,就應該選擇使用State模式。測試
作法:spa
1.使用Self Encapsulate Field將類型碼自我封裝起來。code
2.新建一個類,根據類型碼的用途爲它命名。這就是一個狀態對象。對象
3.爲這個新類添加子類,每一個子類對應一種類型碼。繼承
à比起逐一添加,一次性加入全部必要的子類可能更簡單些。get
4.在超類中創建一個抽象的查詢函數,用以返回類型碼。在每一個子類中覆寫該函數,返回確切的類型碼。it
5.編譯。
6.在源類中創建一個字段,用以保存新建的狀態對象。
7.調整源類中負責查詢類型碼的函數,將查詢動做轉發給狀態對象。
8.調整源類中爲類型碼設值的函數,將一個恰當的狀態對象子類賦值給「保存狀態對象」的那個字段。
9.編譯,測試。
範例:
和上一項重構同樣,咱們仍然使用「僱員/薪資」例子。一樣的,咱們以Employee表示「僱員」:
class Employee{ private int _type; static final int ENGINEER = 0; static final int SALESMAN = 1; static final int MANAGER =2; Employee (int type){ _type = type; } }
下面的代碼展現使用這些類型碼的條件表達式:
int payAmount(){ switch (_type){ case ENGINEER: return _monthlySalary; case SALESMAN: return _monthlySalary + _commission; case MANAGER: return _monthlySalary + _bonus; default: return new RuntimeException("Incorrect Employee"); } }
假設這是一家激情四溢、積極進取的公司,他們能夠將表現出色的工程師擢升爲經理。所以,對象的類型碼是可變的,因此咱們不能使用繼承的方式來處理類型碼。和之前同樣,咱們的第一步仍是使用Self Encapsulate Field將表示類型碼的字段自我封裝起來:
Employee(int type){ setType(type); } int getType(){ return _type; } void setType(int arg){ _type = type; } int payAmount(){ switch (getType()){ case ENGINEER: return _monthlySalary; case SALESMAN: return _monthlySalary + _commission; case MANAGER: return _monthlySalary + _bonus; default: return new RuntimeException("Incorrect Employee"); } }
如今,咱們須要聲明一個狀態類。咱們把它聲明爲一個抽象類,並提供一個抽象函數,用以返回類型碼:
abstract class EmployeeType{ abstract int getTypeCode(); }
如今能夠開始創造子類了:
class Engineer extends EmployeeType{ int getTypeCode(){ return Employee.ENGINEER; } } class Manager extends EmployeeType{ int getTypeCode(){ return Employee.MANAGER; } } class Salesman extends EmployeeType{ int getTypeCode(){ return Employee.SALESMAN; } }
如今進行一次編譯。前面所作的修改實在太平淡了。如今咱們開始修改類型碼訪問函數,實實在在地把這些子類和Employ類聯繫起來:
class Employee... private EmployeeType _type; int getType(){ return _type.getTypeCode(); } void setType(int arg){ switch(arg){ case ENGINEER: _type = new Engineer(); break; case SALESMAN: _type = new Salesman(); break; case MANAGER: _type = new Manager(); break; default: throw new IllegalArgumentException("Incorrect Employee Code"); } }
這意味着咱們將在這裏擁有一個switch語句。完成重構以後,這將是代碼中惟一的switch語句,而且只在對象類型發生改變時纔會執行。咱們也能夠運用Replace Constructor with Factory Method針對不一樣的case子句創建相應的工廠函數。還能夠馬上再使用Replace Conditional with Polymorphism,從而將其餘的case子句徹底消除。
最後,能夠將全部關於類型碼和子類的知識都移到新類,並依此結束Replace Type Code with State/Strategy。首先,咱們把類型碼的定義複製到EmployeeType去,在其中創建一個工廠函數以生成適當的EmployeeType對象,並調整Employee中爲類型碼賦值的函數:
class Employee... void setType(int arg){ _type = EmployeeType.newType(arg); } calss EmployeeType... static EmployeeType newType(int code){ switch(code){ case ENGINEER: return new Engineer(); case SALESMAN: return new Salesman(); case MANAGER: return new Manager(); default: throw new IllegalArgumentException("Incorrect Employee Code"); } } static final int ENGINEER = 0; static final int SALESMAN = 1; static final int MANAGER = 2;
而後,刪掉Employee中的類型碼定義,代之以一個紙箱EmployeeType對象的引用:
class Employee... int payAmount(){ switch (getType()){ case EmployeeType.ENGINEER: return _monthlySalary; case EmployeeType.SALESMAN: return _monthlySalary + _commission; case EmployeeType.MANAGER: return _monthlySalary + _bonus; default: return new RuntimeException("Incorrect Employee"); } }
如今,完事具有,能夠運用Replace Conditional with Polymorphism來處理payAmount()函數了。