Builder
2017-08-28 本文已影响0人
卡农Lucas
序
最近闲着无聊,研究了一下Mybatis的源码,发现有如下的设计方式。
package org.apache.ibatis.mapping;
import javax.sql.DataSource;
import org.apache.ibatis.transaction.TransactionFactory;
public final class Environment {
private final String id;
private final TransactionFactory transactionFactory;
private final DataSource dataSource;
public Environment(String id, TransactionFactory transactionFactory, DataSource dataSource) {
if (id == null) {
throw new IllegalArgumentException("Parameter 'id' must not be null");
}
if (transactionFactory == null) {
throw new IllegalArgumentException("Parameter 'transactionFactory' must not be null");
}
this.id = id;
if (dataSource == null) {
throw new IllegalArgumentException("Parameter 'dataSource' must not be null");
}
this.transactionFactory = transactionFactory;
this.dataSource = dataSource;
}
public static class Builder {
private String id;
private TransactionFactory transactionFactory;
private DataSource dataSource;
public Builder(String id) {
this.id = id;
}
public Builder transactionFactory(TransactionFactory transactionFactory) {
this.transactionFactory = transactionFactory;
return this;
}
public Builder dataSource(DataSource dataSource) {
this.dataSource = dataSource;
return this;
}
public String id() {
return this.id;
}
public Environment build() {
return new Environment(this.id, this.transactionFactory, this.dataSource);
}
}
public String getId() {
return this.id;
}
public TransactionFactory getTransactionFactory() {
return this.transactionFactory;
}
public DataSource getDataSource() {
return this.dataSource;
}
}
疑惑
- 为什么这个
Environment
类定义了一个内部的Builder
类? - 为什么通过这个内部
Builder
类对外部类的赋值? - 这么做是不是多此一举?
解惑
- 之前看到一篇提问,提问者问,为什么建议使用静态的工厂类创建对象?
- 而这个回答者,正是Effective Java的作者,java集合框架的架构师Joshua Bloch。
- 于是,我就京东买了这本书。
- 正巧第一章第二条命中了我的疑问。
何为构建器
- 第二条是这么说的,多个构造器参数时考虑使用构建器。
Why
重载构造函数
- 通常我们会创建多个重载的构造函数来初始化对象。
- 然而当参数个数不断膨胀,客户端代码就会变得异常难写。
问题
- 怎么用?
- 可读性?
- 赋值顺序手误了,
consturctor(a, b)
,极有可能复制成b, a。
JavaBean模式
- 私有域。
- setter和getter赋值。
问题
- 你可能在好几个地方赋值。
- 这就导致参数可能不一致。
- 而且不能声明immutable的类型,因为setter强制私有域不能为final。
怎么办
构建器:既能保持一致性,又能将类定义为期待的样子
- 定义一个共有的内部构建器。
- 构建器对参数进行赋值。
package com.lucas;
public class LucasLuo {
private final String name;
private final String sex;
private final int age;
private final int height;
private final int weight;
private final boolean single;
private final String mobile;
private final String qq;
private final String wechat;
private final String email;
public static class Builder {
private String name;
private String sex;
private int age;
private int height;
private int weight;
private boolean single;
private String mobile;
private String qq;
private String wechat;
private String email;
public Builder name(String name) {
this.name = name;
return this;
}
public Builder sex(String sex) {
this.sex = sex;
return this;
}
public Builder age(int age) {
this.age = age;
return this;
}
public Builder height(int height) {
this.height = height;
return this;
}
public Builder weight(int weight) {
this.weight = weight;
return this;
}
public Builder single(boolean single) {
this.single = single;
return this;
}
public Builder mobile(String mobile) {
this.mobile = mobile;
return this;
}
public Builder qq(String qq) {
this.qq = qq;
return this;
}
public Builder wechat(String wechat) {
this.wechat = wechat;
return this;
}
public Builder email(String email) {
this.email = email;
return this;
}
public LucasLuo build() {
return new LucasLuo(this);
}
}
private LucasLuo(Builder builder) {
this.name = builder.name;
this.sex = builder.sex;
this.age = builder.age;
this.height = builder.height;
this.weight = builder.weight;
this.single = builder.single;
this.mobile = builder.mobile;
this.qq = builder.qq;
this.wechat = builder.wechat;
this.email = builder.email;
}
public String getName() {
return name;
}
public String getSex() {
return sex;
}
public int getAge() {
return age;
}
public int getHeight() {
return height;
}
public int getWeight() {
return weight;
}
public boolean getSingle() {
return single;
}
public String getMobile() {
return mobile;
}
public String getQq() {
return qq;
}
public String getWechat() {
return wechat;
}
public String getEmail() {
return email;
}
}
package com.lucas;
public class LucasMain {
public static void main(String[] args) {
LucasLuo lucasluo =
new LucasLuo
.Builder()
.name("Lucas Luo")
.age(20)
.sex("男")
.height(175)
.weight(70)
.single(true)
.qq("123456")
.wechat("lucasluo")
.email("lucasluo@somesite.com")
.build();
System.out.println(lucasluo.getName());
}
}
总结
当参数有很多个的时候,使用构建器替代静态工厂或重载构造函数是一个不错的办法。
彩蛋
电脑里钻进了几只小蟑螂,怎么抠也抠不出来。
还好,写了一段代码,起了四个线程一直跑。
终于把蟑螂热出来了,碾死。
Done