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模式。

相关文章
相关标签/搜索