Replace Constructor with Factory Method (以工厂函数取...

匆匆过客 提交于 2020-03-02 02:42:48

Summary: 

你希望在创建对象时不仅仅是做简单的建构动作。将构造函数替换为工厂函数。

Motivation: 

使用该手法最显而易见的动机是在派生子类的过程中以工厂函数取代类型码。你可能常常需要根据类型码创建相应的对象,现在,创建名单上还得加上子类,那些子类也是根据类型码来创建。然而由于构造函数只能返回单一类型的对象,因此需要将构造函数替换为工厂函数。

此外,如果构造函数的功能不能满足需要,也可以使用工厂函数来代替它。工厂函数也是Change Value to Reference的基础。可以令工厂函数根据参数的个数和类型,选择不同的创建行为。

Mechanics:

 1.新建一个工厂函数,让它调用现有的构造函数。

2.将调用构造函数的代码改为调用工厂函数。

3.每次替换后编译并测试

4.将构造函数声明为private

5.编译

范例1. 根据整数(实际是类型码)创建对象

         以员工薪资系统为例,“Employee”表示“员工”:

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




易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!