Java Builder Pattern
发表于|更新于
|阅读量:
静态工厂和构造函数有一个共同的限制:它们不能很好地扩展到大量可选参数,所以考虑通过构造器来构造一个类的实例。
<The rest of contents | 余下全文>
Telescoping constructor pattern
通常我们在创建一个类的实例时候都是用它的构造函数来创建。例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| public class NutritionFacts { private final int servingSize; private final int servings; private final int calories; private final int fat; private final int sodium; private final int carbohydrate;
public NutritionFacts(int servingSize, int servings) { this(servingSize, servings, 0); }
public NutritionFacts(int servingSize, int servings, int calories) { this(servingSize, servings, calories, 0); }
public NutritionFacts(int servingSize, int servings, int calories, int fat) { this(servingSize, servings, calories, fat, 0); }
public NutritionFacts(int servingSize, int servings, int calories, int fat, int sodium) { this(servingSize, servings, calories, fat, sodium, 0); }
public NutritionFacts(int servingSize, int servings, int calories, int fat, int sodium, int carbohydrate) { this.servingSize = servingSize; this.servings = servings; this.calories = calories; this.fat = fat; this.sodium = sodium; this.carbohydrate = carbohydrate; } }
|
1 2
| NutritionFacts cocaCola = new NutritionFacts(240, 8, 100, 0, 35, 27);
|
但是这种方式存在很多不便之处:
- 即使只需要设定一部分参数也要为不需要设置的参数设置默认值
- 当参数过多的时候,尤其是存在许多同类型的参数的时候,很容易产生参数设置的混淆,这种混淆编译是不会发现的,在程序运行的时候才会产生错误
- 代码不易读懂,用户使用函数时候需要参照api
JavaBeans Pattern
另外一种可选的方式是javabean的形式,其主要理念是通过空构造函数来创建一个实例,然后通过set方法对参数进行赋值。例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| public class NutritionFacts { private int servingSize = -1; private int servings = -1; private int calories = 0; private int fat = 0; private int sodium = 0; private int carbohydrate = 0;
public NutritionFacts() { }
public void setServingSize(int val) { servingSize = val; }
public void setServings(int val) { servings = val; }
public void setCalories(int val) { calories = val; }
public void setFat(int val) { fat = val; }
public void setSodium(int val) { sodium = val; }
public void setCarbohydrate(int val) { carbohydrate = val; } }
|
这种模式没有Telescoping constructor pattern的任何缺点。
创建实例很容易,虽然有点冗长,并且很容易读取生成的代码:
1 2 3 4 5 6
| NutritionFacts cocaCola = new NutritionFacts(); cocaCola.setServingSize(240); cocaCola.setServings(8); cocaCola.setCalories(100); cocaCola.setSodium(35); cocaCola.setCarbohydrate(27);
|
但是,javabean模式本身有严重的缺点。因为构造是在多个调用之间分割的,所以JavaBean在构造过程中可能处于不一致的状态。
Builder pattern
客户机不直接生成所需的对象,而是使用所有必需的参数调用构造函数(或静态工厂)并获得一个构建器对象。然后客户端调用builder对象上类似setter的方法来设置每个可选参数。最后,客户端调用一个无参数的构建方法来生成对象,该对象通常是不可变的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
|
public class NutritionFacts { private final int servingSize; private final int servings; private final int calories; private final int fat; private final int sodium; private final int carbohydrate;
public static class Builder { private final int servingSize; private final int servings; private int calories = 0; private int fat = 0; private int sodium = 0; private int carbohydrate = 0;
public Builder(int servingSize, int servings) { this.servingSize = servingSize; this.servings = servings; }
public Builder calories(int val) { calories = val; return this; }
public Builder fat(int val) { fat = val; return this; }
public Builder sodium(int val) { sodium = val; return this; }
public Builder carbohydrate(int val) { carbohydrate = val; return this; }
public NutritionFacts build() { return new NutritionFacts(this); } }
private NutritionFacts(Builder builder) { servingSize = builder.servingSize; servings = builder.servings; calories = builder.calories; fat = builder.fat; sodium = builder.sodium; carbohydrate = builder.carbohydrate; }
public static void main(String[] args) { NutritionFacts cocaCola = new NutritionFacts.Builder(240, 8) .calories(100).sodium(35).carbohydrate(27).build(); } }
|
Builder pattern也有缺点。为了创建对象,必须首先创建它的构建器。虽然在实践中创建这个构建器的成本不太可能显著,但在性能关键的情况下,它可能会成为一个问题。此外,Builder pattern的代码较为冗长,在设计构造函数或静态工厂有多个参数的类时,尤其是当许多参数是可选的或具有相同类型时,Builder模式是一个很好的选择,因为它更易读、更安全。