Replace Type Code with State/Strategy

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()函數了。

相關文章
相關標籤/搜索