Replace Constructor with Factory Method (以工廠函數取...

Summary: 

你但願在建立對象時不只僅是作簡單的建構動做。將構造函數替換爲工廠函數。 java

Motivation: 

使用該手法最顯而易見的動機是在派生子類的過程當中以工廠函數取代類型碼。你可能經常須要根據類型碼建立相應的對象,如今,建立名單上還得加上子類,那些子類也是根據類型碼來建立。然而因爲構造函數只能返回單一類型的對象,所以須要將構造函數替換爲工廠函數。 安全

此外,若是構造函數的功能不能知足須要,也可使用工廠函數來代替它。工廠函數也是Change Value to Reference的基礎。能夠令工廠函數根據參數的個數和類型,選擇不一樣的建立行爲。 函數

Mechanics:

 1.新建一個工廠函數,讓它調用現有的構造函數。 測試

2.將調用構造函數的代碼改成調用工廠函數。 this

3.每次替換後編譯並測試 spa

4.將構造函數聲明爲private 設計

5.編譯 code

範例1. 根據整數(實際是類型碼)建立對象

         以員工薪資系統爲例,「Employee」表示「員工」: orm

class Employee{
    private int type;
    static final int ENGINEER = 0;
    static final int SALESMAN = 1;
    static final int MANAGER = 2;
    Employee(int type){
       this.type = type;
   }
}
 咱們但願爲Employee提供不一樣的子類,並分別給予它們相應的類型碼。所以,須要創建一個工廠函數:
static Employee create(int type){
   return new Employee(type);
}


而後,修改構造函數的全部調用點,讓它們改用上述新建的工廠函數,並將構造函數聲明爲private: 對象

client code:

Employee eng = Employee.create(Employee.ENGINEER);



class Employee{
    ...
    private Employee(int type){
        this.type = type;
    }
}

範例2 根據字符串建立子類對象


目前爲止,咱們並無得到什麼實質收穫。目前的好處在於:「對象建立請求的接收者」和「被建立對象所屬的類」被分開了。若是隨後使用Replace Type Code with Subclasses把類型碼轉換爲Employee的子類,就課能夠運用工廠函數,將這些子類對用戶隱藏起來:


static Employee create(int type){
   switch(type){
     case ENGINEER:
        return new Engineer();
     case SALESMAN:
        return new Salesman();
     case MANAGER:
        return new Manager();
     default:
        throw new IllegalArgumentException("Incorrect type code value");
   }
}
惋惜的是,這裏面有一個switch語句。若是咱們添加一個新的子類,就必須記得更新這裏的switch語句。


繞過這個switch語句的一個好辦法是使用Class.forName().第一件要作的事是修改參數類型,這從根本上說是Rename Method的一種變體。首先咱們得創建一個函數,讓它接收一個字符串參數:


static Employee create(String name){
   try{
       return (Employee)Class.forName(name).newInstance();
    }catch(Exception e){
       throw new IllegalArgumentException("Unable to instantiate " + name);
   }
}
而後讓稍早那個"create()函數int版"調用新的"create()函數String版":
static Employee create(int type){
   switch(type){
     case ENGINEER:
        return create("Engineer");
     case SALESMAN:
        return create("Salesman");
     case MANAGER:
        return create("Manager");
     default:
        throw new IllegalArgumentException("Incorrect type code value");
   }
}


而後,修改create()函數的調用者,將下列這樣的語句:

Employee.create(ENGINEER)


修改成:

Employee.create("Engineer");


完成以後,就能夠將"create()函數int版"移除了。

如今,當咱們須要添加新的Employee子類時,就不須要更新create()函數了。可是卻所以失去了編譯期檢查,使得一個小小的拼寫錯誤就可能形成運行期錯誤。若是要防止運行期錯誤,可使用明確函數來建立對象。但這樣一來,沒添加一個新的子類,就必須添加一個新的函數。這就是爲了類型安全而犧牲掉的靈活性。

另外一個必須謹慎使用Class.forName()的緣由是:它像用戶暴露了子類名稱。

範例3 以明確函數建立子類

咱們能夠經過另外一條途徑來隱藏子類——使用明確函數。若是隻有少數幾個子類,並且它們都再也不變化,這個方法是頗有用的。有個抽象的Person類,它有兩個子類:Male和Female。首先咱們在超類中爲每一個子類定義一個工廠函數:

class Person{
  ...
  static Person createMale(){
      return new Male();
  }
  static Person createFemale(){
      return new Female();
  }
}

而後咱們能夠把下面的調用:

Person kent = new Male();

替換成:

Person kent = Person.createMale();

可是這就使得超類必須知曉子類。若是想避免這種狀況,就須要一個更爲複雜的設計,例如Product Trader模式。

相關文章
相關標籤/搜索