Java建造者模式解读

Builder模式是代码编写过程中经常会用到的一类设计模式。最近重读了《Effective Java》和《设计模式》的builder章节,同时也读了其他人对于builder模式的理解和应用,在此记录我自己对Builder模式的一些理解。

一、意图

将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

二、场景

1.当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时。
2.当构造过程必须允许被构造的对象有不同的表示时。

三、实例

遇到多个构造器参数时要考虑用构建器。静态工厂和构造器有个共同的局限性:它们都不能很好地扩展到大量的可选参数。
考虑这样的一个场景:用一个类表示包装食品外面显示的营养成分标签。这些标签中有几个域是必需的:每份的含量、每罐的含量以及每份的卡路里,还有超过20个可选域:总脂肪量、饱和脂肪量、转化脂肪、胆固醇、钠等等。
程序员一向习惯采用重叠构造器模式,在这种模式下,你提供第一个只有必要参数的构造器,第二个构造器有一个可选参数,第三个有两个可选参数,以此类推,最后一个构造器包含所有可选参数。重叠构造器模式可行,但是当有许多参数的时候,客户端代码会很难编写,并且仍然难以阅读。一长串类型相同的参数会导致一些微妙的错误。如果客户端不小心颠倒了其中两个参数的顺序,编译器也不会出错,但是程序在运行时会出现错误的行为。

下面是Builder模式的代码:

1
2
3
public interface IBuilder<T> {
public T build();
}

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

private NutritionFacts(final Builder builder) {
this.servingSize = builder.servingSize;
this.servings = builder.servings;
this.calories = builder.calories;
this.fat = builder.fat;
this.sodium = builder.sodium;
this.carbohydrate = builder.carbohydrate;
}

@Override
public String toString() {
return "NutritionFacts{" + "servingSize=" + this.servingSize + ", servings=" + this.servings + ", calories="
+ this.calories + ", fat=" + this.fat + ", sodium=" + this.sodium + ", carbohydrate="
+ this.carbohydrate + '}';
}

public static class Builder implements IBuilder<NutritionFacts> {
// 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 sodium = 0;
private int carbohydrate = 0;

public Builder(final int servingSize, final int servings) {
this.servingSize = servingSize;
this.servings = servings;
}

public Builder calories(final int val) {
this.calories = val;
return this;
}

public Builder fat(final int val) {
this.fat = val;
return this;
}

public Builder carbohydrate(final int val) {
this.carbohydrate = val;
return this;
}

public Builder sodium(final int val) {
this.sodium = val;
return this;
}

@Override
public NutritionFacts build() {
return new NutritionFacts(this);
}
}
}

NutritionFacts是不可变的。builder的setter方法返回builder本身,以便可以把调用链接起来。下面就是客户端代码:

1
2
3
4
5
6
7
8
9
10
11
public class BuilderDemo {
public static void main(final String[] args) {
final IBuilder<NutritionFacts> builder = new NutritionFacts.Builder(5, 3).calories(1000);
final NutritionFacts nutritionFacts = createNutritionFacts(builder);
System.out.println(nutritionFacts);
}

private static NutritionFacts createNutritionFacts(final IBuilder<NutritionFacts> builder) {
return builder.build();
}
}

这样的客户端代码很容易编写,更重要的是,易于阅读。

四、个人理解

builder模式要点

1.用于分步骤构建一个复杂的对象。builder提供几种固定的步骤接口和获取最终对象接口,这些步骤接口的调用次数、调用顺序由Director决定,最终通过获取最终对象接口得到最终产品。

2.支持多种构建算法。不同构件算法可以创建出不同的表现。Builder模式封装了构建算法,调用者无需知道具体的构建算法细节。

3.拥有稳定的部件类型及部件装配方式。builder接受的组件类型和组件装配方式稳定不变,构建算法和它的组件互不影响。

builder模式优点

1.将一个“复杂对象的构建算法”与它的“部件及组装方式”分离,使得构建算法可以独立应对变化;

2.不同构件算法可以复用相同的部件和部件组装方式;

3.不同构件算法,可以生成不同表现的对象;

4.“部件类型“及其”组装方式“相对稳定,不随着构建算法改变而改变。

总结

理解builder模式重点在两个词,“创建对象”和“一变一不变”。
“创建对象”指明builder模式的最终目的为创建对象,是大前提。“一变一不变”即构建算法多变,但是部件和部件装配方式稳定不变,既可以用于描述builder模式使用场景,也是builder模式的特点。
案例代码是可以运行的,自己敲一敲。至少要考下来放到ide中运行下,分析下来帮助你理解和吸收。

参考:
1.《大话设计模式》
2.《Effective Java 第二版》

dalaoyan wechat
扫一扫,用手机访问本站
-------------本文结束 感谢您的阅读-------------
作者dalaoyan
有问题请 留言 或者私信我的 微博
满分是10分的话,这篇文章你给几分