定义
将一个复杂对象的创建与表示分离,使得同样的创建过程可以创建不同的表示
演化
1.定义一个电脑类,并提供给外界创建它的方法,我们通常会这么做
public class Computer {
private String cpu;
private String mainboard;
public Computer(String cpu) {
this.cpu = cpu;
}
public Computer(String cpu, String mainboard) {
this.cpu = cpu;
this.mainboard= mainboard;
}
// 省略getter和setter
}
调用者创建对象时,按照规则传入参数即可。但这里依然存在些许小问题。
(1) 调用者不需要cpu,想要只传入一个mainboard参数来构建对象。如何提供相应的构造函数?你会发现这么一个简单的问题根本无法入手,因为cpu和mainboard都是String类型。单独构建时,其构造方法签名是一致的。因此无法提供单独构建mainboard。调用者只能使用代码中给出的双参数构造函数,构建对象时,不需要的参数传null。
public void build() {
Computer computer = new Computer(null, "mainboard");
}
(2) 在(1)的前提下,调用者需要知道构造方法中,每个参数的意义。如果创建时,先传mainboard,再传null,肯定会产生错误。代码的可读性也不高,以后接手的人需要先了解Computer的构造方法,才能知道这里每个传入参数的意义。
当然在仅仅只有两个的参数的时候,这两个小问题都可以忽略不计。调用处多传一个null参数,创建成本不算高。记住两个参数的意义也是小菜一碟。而且现在的开发工具都附带一定的提示功能,代码阅读也没有太大问题。
2.现在我们扩展这个电脑类,然后再考虑1中遗留的问题
public class Computer {
private String cpu;
private String mainBoard;
private String memory;
private String displayAdapter;
private String networkAdapter;
private String monitor;
private String hdd;
private String fdd;
private String mouse;
private String keyboard;
// 省略getter和setter
}
调用者传入的参数十分灵活,可能只是其中任意的一个或几个。这个时候你会发现想要提供灵活的构造方法根本无从下手:构造参数的排列组合情况过多,当参数数量相同时,还会产生冲突。想要通过构造函数兼容所有的情况,就只能提供一个包含全部属性的构造函数了。
public Computer(String cpu, String mainBoard, String memory, String displayAdapter, String networkAdapter, String monitor, String hdd, String fdd, String mouse, String keyboard) {
this.cpu = cpu;
this.mainBoard = mainBoard;
this.memory = memory;
this.displayAdapter = displayAdapter;
this.networkAdapter = networkAdapter;
this.monitor = monitor;
this.hdd = hdd;
this.fdd = fdd;
this.mouse = mouse;
this.keyboard = keyboard;
}
这个构造函数有10个参数,调用者要知道每个位置对应的参数,才能把自己的参数传到指定位置,对于不需要的参数还要传入null。此时创建一个参数的成本已经很高了,而且极易出错,这点在构造函数仅有三四个参数时就有可能出现了,更何况是10个乃至更多参数。即便是费劲写好了构造过程,换个人来阅读这段代码时,理解起来也会很有难度。
总结,在创建一个多参数类的对象时,会存在以下问题
(1) 定义者只能提供有限的构造方法
(2) 调用者需要充分了解类的构造方法,并选择自己需要的。
(3) 调用者创建对象成本较高,还会产生冗余代码。
(4) 调用者对于构造方法的使用容易出错
(5) 调用处的代码可读性不高
3.考虑前面所说的问题,我们在定义类仅指定默认构造方法和关键参数的构造方法,让调用者自己手动调用setter去设置值。使用这种方式倒是无需定义繁琐的构造方法,调用者也可以灵活控制参数而无需过多了解构造方法,并且可以通过setter的方法名清晰的表达设置的意图,提高代码的可读性。这也是我们平时创建一个多参数对象的常见写法了,大多数情况下都是这么做的,也很少出问题。因为我们创建的所谓复杂对象,仅仅是参数多点罢了。
public void build() {
Computer computer = new Computer();
computer.setCpu(&