[Java] Multiple parameters in a constructor

在開發的過程中很常會碰到需要傳入大量的參數到建構子的情況,這樣做法會讓其他人不易理解,造成維護和測試上的困難。在Clean Code: A Handbook of Agile Software Craftsmanship有提到:函式的參數最好不要超過三個。

那這樣的情況應該要怎麼做會比較好呢?可以利用Effective Java (2nd Edition)中所提到的Builder pattern來解決。

並不是每種情況都適合使用Builder pattern來解決,Builder pattern是解法之一。

範例:

還沒有使用Builder 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; // (mL) required
private final int servings; // (per container) required
private final int calories; // optional
private final int fat; // (g) optional
private final int sodium; // (mg) optional
private final int carbohydrate; // (g) optional

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;
}
}

使用Builder 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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
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 {
// Required parameters
private final int servingSize;
private final int servings;
// Optional parameters - initialized to default values
private int calories = 0;
private int fat = 0;
private int carbohydrate = 0;
private int sodium = 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 carbohydrate(int val) {
carbohydrate = val;
return this;
}

public Builder sodium(int val) {
sodium = 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;
}
}

initialize NutritionFacts:

1
2
NutritionFacts sodaDrink = new NutritionFacts.Builder(240, 8).
calories(100).sodium(35).carbohydrate(27).build();

比較:

優點:

  1. 增加可讀性。
  2. 讓物件為immutable。

缺點:

  1. builder和object的程式碼重複性很高。
  2. 新增或刪除object的attribute,需要回頭修改builder程式碼。例如:同時要Override object和builder的equals(),hashCode()。

這裡的Builder pattern和Design Patterns: Elements of Reusable Object-Oriented Software中所提到的Builder pattern似乎不一樣,但是概念上很相近。後者會有一個共用組裝和建構Product的interface;前者因為組裝和建構流程差異過大,所以沒有共用的interface。所以前者很難做到抽換不同的Concrete Builder就可以產生不同的的Product。

參考資料: