Builder模式作为创建型模式之一,几乎随处可见,尤其是创建复杂对象时;每当构造方法不能再愉快地工作时,可能就需要它出马拯救即将”崩坏““丑化”的代码。
模式概况
- 实际需求
- 想创建的目标对象所需参数较多,且很多参数可能需要赋予默认值时
- 想创建的目标对象所需参数并不需要固定的顺序时
- 想以多种方式创建不同属性的目标对象却又不想使用多个构造方法时
- 想创建的目标对象有可选参数时
- 解决方案
- 分离构建操作,使参数”可变“
- 详细设计
- 传入方法类似于settter,不过是由第三方传入并产生真正的对象,在此之前对象应无法进行调用
builder模式在Python中其实并无用武之地,因为Python本身提供了参数赋值较为灵活的语法,可以自由指定参数,而Java的语法已经规定只有最后一个参数可以写成可变长参数,这也大大限制了构造方法的多变性。
比如Python可以这样写构造方法:
class Point:
def __init__(self, x=0, y=0, z=0, desc="complete point"):
self.x = x
self.y = y
self.z = z
self.desc = desc
def display(self):
print(self.x, self.y, self.z, self.desc)
p1 = Point(1, 2)
p1.display()
p2 = Point(y=2, z=3)
p2.display()
p3 = Point(x=1, desc="x axis")
p3.display()
输出分别为:
1 2 0 complete point
0 2 3 complete point
1 0 0 x axis
而这种写法在Java中几乎只能在注解中出现,但那无济于事;
@Documented
@Target({ElementType.METHOD,ElementType.FIELD,ElementType.PARAMETER,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface Caution {
String value1() default "m";
int value2() default 10;
String value3();
}
Java中也有可变长参数,但限制太多,一是只能有一个可变长参数且必须在方法末尾;二是这个参数类型必须是固定的;
所以Java想实现类似效果,还得靠设计;简而言之,就是不再使用构造方法一次性将所有参数赋值,而是化整为零地对参数进行赋值。
这个过程就像实现生活中的产品组装,比如汽车,比如电脑,各零配件的选配组合成型,全程总是由第三方的一个组装者完成操作,所以叫作Builder模式。
模式实现
实际使用当中往往直接使用Builder自由组装,将整个模式精简为一个类,产品、组装者一起存在:
public class Point {
private int x;
private int y;
private int z;
private String desc;
private Point(Builder builder){
x = builder.x;
y = builder.y;
z = builder.z;
desc = builder.desc;
}
public static class Builder{
private int x = 0;
private int y = 0;
private int z = 0;
private String desc = "nice";
public Builder setX(int x){
this.x = x;
return this;
}
public Builder setY(int y){
this.y = y;
return this;
}
public Builder setZ(int z){
this.z = z;
return this;
}
public Builder setDesc(String desd){
this.desc = desd;
return this;
}
public Point build(){
return new Point(this);
}
}
}
使用是这样的:
public class Main {
public static void main(String[] args) {
Point point = new Point.Builder()
.setX(1)
.setY(2)
.setDesc("Haha")
.build();
System.out.println(point);
}
}
这种精简的Builder模式,整体就是突出精简与自由;
而传统的Builder模式,有三个重要角色,分别为产品、组装者、监工;
首先是产品, 也可以根据实际情况再抽象出一层抽象产品类并继承,这里产品零件(参数)较少,不再提及:
public class Point {
private int x;
private int y;
private int z;
private String desc;
public void setX(int x) {
this.x = x;
}
public void setY(int y) {
this.y = y;
}
public void setZ(int z) {
this.z = z;
}
public void setDesc(String desc) {
this.desc = desc;
}
@Override
public String toString() {
return "Point{" +
"x=" + x +
", y=" + y +
", z=" + z +
", desc='" + desc + '\'' +
'}';
}
}
再者是组装者Builder,同样可以抽象出一层,这里同样因为组装流程较少不提及:
public class PointBuilder {
private Point point = new Point();
public void buildX(int x) {
point.setX(x);
}
public void buildY(int y) {
point.setY(y);
}
public void buildZ(int z) {
point.setZ(z);
}
public void buildDesc(String desc) {
point.setDesc(desc);
}
public Point create() {
return point;
}
}
最后是监工,也是组装者工作的地方:
public class PointDirector {
private PointBuilder mBuilder;
public PointDirector(PointBuilder builder) {
this.mBuilder = builder;
}
public void work() {
mBuilder.buildX(2);
mBuilder.buildX(3);
mBuilder.buildZ(4);
mBuilder.buildDesc("nice");
}
}
具体使用:
public class Main {
public static void main(String[] args) {
PointBuilder builder = new PointBuilder();
PointDirector director = new PointDirector(builder);
director.work();
System.out.println(builder.create());
}
}
用表表示出来是:
类 | 说明 |
---|---|
Point | 产品/对象 |
PointBuilder | 组装者 |
PointDirector | 监工,组装者工作之地 |
Main | 最终需要调用产品/对象的地方 |
图表示出来是:
可以看到,传统的写法步骤较多,角色也相对复杂,不如精简模式来得巧妙简洁;但传统写法有利于对Builder模式的原生理解,因此也需要有所掌握。