静态工厂和构造器有一个共同的局限性:不能很好地扩展到大量的可选参数
一个Book类有两个必需标签name和author,三个非必需标签price、isbn和press,对于这样的类可以采用三种方式编写:
- 重叠构造器
- JavaBeans模式
- 建造者模式
1. 重叠构造器
重叠构造器提供的第一个构造器只有必要的参数,第二个构造器有一个可选参数,第三个构造器有两个可选参数,以此类推
class Book {
private String name;
private String author;
private int price;
private String isbn;
private String press;
public Book(String name, String author) {
this(name, author, 0);
}
public Book(String name, String author, int price) {
this(name, author, price, "");
}
public Book(String name, String author, int price, String isbn) {
this(name, author, price, isbn, "");
}
public Book(String name, String author, int price, String isbn, String press) {
this.name = name;
this.author = author;
this.price = price;
this.isbn = isbn;
this.press = press;
}
}
自行调试
public class Test {
public static void main(String[] args) {
Book book = new Book("人生海海","麦家",0,"","北京十月文艺出版社");
}
}
重叠构造器可行,但是可读性差
2. JavaBeans模式
先使用无参构造器创建对象,再调用setter方法来设置每个必要的参数
class Book {
private String name = "";
private String author = "";
private int price = 0;
private String isbn = "";
private String press = "";
public Book() {
}
public void setName(String name) {
this.name = name;
}
public void setAuthor(String author) {
this.author = author;
}
public void setPrice(int price) {
this.price = price;
}
public void setIsbn(String isbn) {
this.isbn = isbn;
}
public void setPress(String press) {
this.press = press;
}
}
自行调试
public class Test {
public static void main(String[] args) {
Book book = new Book();
book.setName("人生海海");
book.setAuthor("麦家");
book.setPress("北京十月文艺出版社");
}
}
可读性好,创建实例简单,但是JavaBeans模式有两个缺点
- 构造过程分到了几个调用中,可能出现诸如线程A正在调用set设置某对象的参数同时线程B调用get使用该对象的情况,无法保证一致性
- 提供setter方法使得类失去了做成不可变的可能
3. 建造者模式
①利用必要参数调用构造器,得到builder对象
②对builder对象调用类似setter的方法设置可选参数
③调用无参的build方法生成对象
class Book {
private final String name;
private final String author;
private final int price;
private final String isbn;
private final String press;
public static class Builder {
String name;
String author;
int price = 0;
String isbn = "";
String press = "";
public Builder(String name, String author) {
this.name = name;
this.author = author;
}
public Builder setPrice(int price) {
this.price = price;
return this;
}
public Builder setIsbn(String isbn) {
this.isbn = isbn;
return this;
}
public Builder setPress(String press) {
this.press = press;
return this;
}
public Book build() {
return new Book(this);
}
}
private Book(Builder builder) {
this.name = builder.name;
this.author = builder.author;
this.price = builder.price;
this.isbn = builder.isbn;
this.press = builder.press;
}
}
自行调试
public class Test{
public static void main(String[] args) {
Book book = new Book.Builder("人生海海","麦家").setPress("北京十月文艺出版社").build();
}
}
builder的优势
1. 可以将多次调用某一个方法而传入的参数集中到一个域中
简单修改一下press属性及其相关方法
class Book {
private final String name;
private final String author;
private final int price;
private final String isbn;
private final List<String> press;
public static class Builder {
private final String name;
private final String author;
private int price = 0;
private String isbn = "";
private List<String> press = new ArrayList<>();
public Builder(String name, String author) {
this.name = name;
this.author = author;
}
public Builder setPrice(int price) {
this.price = price;
return this;
}
public Builder setIsbn(String isbn) {
this.isbn = isbn;
return this;
}
public Builder addPress(String press) {
this.press.add(press);
return this;
}
public Book build() {
return new Book(this);
}
}
private Book(Builder builder) {
this.name = builder.name;
this.author = builder.author;
this.price = builder.price;
this.isbn = builder.isbn;
this.press = new ArrayList<>(builder.press);
}
}
连续调用两次addPress方法,可以将传入的参数集中到press里面
自行调试
public class Test {
public static void main(String[] args) {
Book book = new Book.Builder("人生海海","麦家").addPress("北京十月文艺出版社").addPress("北京出版集团公司").build();
}
}
2. 可以利用单个builder构建多个对象
自行调试
public class Test {
public static void main(String[] args) {
Book.Builder builder = new Book.Builder("人生海海","麦家").addPress("北京十月文艺出版社").addPress("北京出版集团公司");
Book book1 = builder.build();
Book book2 = builder.build();
}
}
3. 自动填充某些域
创建Book对象时,为图书增加一个序列号
class Book {
private final int count;
private final String name;
private final String author;
private final int price;
private final String isbn;
private final List<String> press;
public static class Builder {
private static int count = 0;
private final String name;
private final String author;
private int price = 0;
private String isbn = "";
private List<String> press = new ArrayList<>();
public Builder(String name, String author) {
this.name = name;
this.author = author;
}
public Builder setPrice(int price) {
this.price = price;
return this;
}
public Builder setIsbn(String isbn) {
this.isbn = isbn;
return this;
}
public Builder addPress(String press) {
this.press.add(press);
return this;
}
public Book build() {
count++;
return new Book(this);
}
}
private Book(Builder builder) {
this.count = Builder.count;
this.name = builder.name;
this.author = builder.author;
this.price = builder.price;
this.isbn = builder.isbn;
this.press = new ArrayList<>(builder.press);
}
public int getCount() {
return this.count;
}
}
自行调试
public class Test {
public static void main(String[] args) {
Book.Builder builder1 = new Book.Builder("人生海海","麦家").addPress("北京十月文艺出版社").addPress("北京出版集团公司");
Book.Builder builder2 = new Book.Builder("Effective Java","Joshua Bloch").addPress("机械工业出版社");
Book book1 = builder1.build();
Book book2 = builder2.build();
Book book3 = builder1.build();
System.out.println(book1.getCount());
System.out.println(book2.getCount());
System.out.println(book3.getCount());
}
}
4. builder的参数可以在调用build方法来创建对象期间进行调整,也可以随着不同的对象而改变
builder的劣势
1. 创建对象之前必须先创建它的构建器
2. 代码更加冗长