- 考虑使用静态工厂方法替代构造方法
下面是一些静态工厂方法的常用名称。以下清单这是列出了其中的一小部分:
// from —— 类型转换方法,它接受单个参数并返回此类型的相应实例,例如:
Date d =Date.from(instant);
//of —— 聚合方法,接受多个参数并返回该类型的实例,并把他们合并在一起,例如:
Set<Rank>faceCards = EnumSet.of(JACK, QUEEN, KING);
//valueOf —— from 和 to 更为详细的替代 方式,例如:
BigInteger prime = BigInteger.valueOf(Integer.MAX_VALUE);
//instance 或 getinstance —— 返回一个由其参数 (如果有的话) 描述的实例,但不能说它具有相同的值,
//例如:
StackWalker luke = StackWalker.getInstance(options);
//create 或 newInstance —— 与 instance 或 getInstance 类似,除此之外该方法保证每次调用返回
//一个新的实例,例如:
Object newArray = Array.newInstance(classObject, arrayLen);
// getType —— 与 getInstance 类似,但是在工厂方法处于不同的类中的时候使用。getType 中的
//Type 是工厂方法返回的对象类型,例如:
FileStore fs = Files.getFileStore(path);
//newType —— 与 newInstance 类似,但是在工厂方法处于不同的类中的时候使用。newType中的Type
// 是工厂方法返回的对象类型,例如:
BufferedReader br = Files.newBufferedReader(path);
//type —— getType 和 newType 简洁的替代方式,例如:
List<Complaint> litany = Collections.list(legacyLitany);
- 当构造方法参数过多时使用 builder 模式
下面是一个可伸缩构造方法
package effective;
/**
* 美团外卖点菜填写信息
* @Author: hyh
* @Date: 2021/9/10 16:27
**/
public class Order {
// 菜名 必填
private final String name;
// 数量 必填
private final int num;
// 地址 必填
private final String address;
// 电话 必填
private final long phone;
// 性别 选填
private final String sex;
// 爱好 选填
private final String hobby;
// 工作 选填
private final String job;
public Order(String name, int num, String address, long phone) {
// 调用下一个
this(name,num,address,phone,"未知");
}
public Order(String name, int num, String address, long phone, String sex) {
this(name,num,address,phone,"未知","未知");
}
public Order(String name, int num, String address, long phone,
String sex, String hobby) {
this(name,num,address,phone,"未知","未知","未知");
}
public Order(String name, int num, String address, long phone,
String sex, String hobby, String job) {
this.name = name;
this.num = num;
this.address = address;
this.phone = phone;
this.sex = sex;
this.hobby = hobby;
this.job = job;
}
public void toPrint() {
System.out.println("Order{" +
"菜名='" + name + '\'' +
", 数量=" + num +
", 收货地址='" + address + '\'' +
", 收货人电话=" + phone +
", 收货人性别='" + sex + '\'' +
", 爱好='" + hobby + '\'' +
", 工作='" + job + '\'' +
'}');
}
public static void main(String[] args) {
// 需要几个参数填几个
Order order = new Order("北京烤鸭", 4, "西安钟楼", 18888888888L);
order.toPrint();
}
}
输出:
Order{菜名='北京烤鸭', 数量=4, 收货地址='西安钟楼', 收货人电话=18888888888,
收货人性别='未知', 爱好='未知', 工作='未知'}
可伸缩构造方法模式是有效的,但是当有很多参数时,很难编写客户端代码,而且很难读懂它。而且读者不知道这些值是什么意思,并且必须仔细地去数参数才能找到答案。一长串相同类型的参数可能会导致一些 bug。如果客户端不小心写反了两个这样的参数,编译器并不会报错,但是程序在运行时会出现与预期不一致的行为
当在构造方法中遇到许多可选参数时,另一种选择是 JavaBeans 模式,在这种模式中,调用一个无参的构造方法来创建对象,然后调用 setter 方法来设置每个必需的参数和可选参数:
package effective;
/**
* 美团外卖点菜填写信息
* @Author: hyh
* @Date: 2021/9/10 16:27
**/
public class Order {
// 菜名 必填
private String name;
// 数量 必填
private int num;
// 地址 必填
private String address;
// 电话 必填
private long phone;
// 性别 选填
private String sex;
// 爱好 选填
private String hobby;
// 工作 选填
private String job;
public Order() {
}
public void setName(String name) {
this.name = name;
}
public void setNum(int num) {
this.num = num;
}
public void setAddress(String address) {
this.address = address;
}
public void setPhone(long phone) {
this.phone = phone;
}
public void setSex(String sex) {
this.sex = sex;
}
public void setHobby(String hobby) {
this.hobby = hobby;
}
public void setJob(String job) {
this.job = job;
}
public void toPrint() {
System.out.println("Order{" +
"菜名='" + name + '\'' +
", 数量=" + num +
", 收货地址='" + address + '\'' +
", 收货人电话=" + phone +
", 收货人性别='" + sex + '\'' +
", 爱好='" + hobby + '\'' +
", 工作='" + job + '\'' +
'}');
}
public static void main(String[] args) {
Order order = new Order();
order.setName("北京烤鸭");
order.setNum(4);
order.setPhone(18888888888L);
order.setAddress("西安钟楼");
order.toPrint();
}
}
由于构造方法被分割成了多次调用,所以在构造过程中 JavaBean 可能处于不一致的状态。 该类仅通过检查构造函数参数的有效性,而没有强制的一致性措施。在不一致的状态下尝试使用对象可能会导致一些错误,这些错误与平常代码的 BUG 很是不同,因此很难调试。
builder模式:结合了可伸缩构造方法模式的安全性和 JavaBean 模式的可读性。客户端不直接构造所需的对象,而是调用一个包含所有必需参数的构造方法 (或静态工厂) 得到获得一个 builder 对象。然后,客户端调用 builder 对象的与setter 相似的方法来设置你想设置的可选参数。最后,客户端调用 builder 对象的一个无参的build 方法来生成对象,该对象通常是不可变的。Builder 通常是它所构建的类的一个静态成员类
package effective;
/**
* 美团外卖点菜填写信息
*
* @Author: hyh
* @Date: 2021/9/10 16:27
**/
public class Order {
// 菜名 必填
private final String name;
// 数量 必填
private final int num;
// 地址 必填
private final String address;
// 电话 必填
private final long phone;
// 性别 选填
private final String sex;
// 爱好 选填
private final String hobby;
// 工作 选填
private final String job;
private Order(Builder builder) {
this.name = builder.name;
this.num = builder.num;
this.address = builder.address;
this.phone = builder.phone;
this.sex = builder.sex;
this.hobby = builder.hobby;
this.job = builder.job;
}
public static class Builder {
// 菜名 必填
private final String name;
// 数量 必填
private final int num;
// 地址 必填
private final String address;
// 电话 必填
private final long phone;
// 性别 选填
private String sex = "未知";
// 爱好 选填
private String hobby = "未知";
// 工作 选填
private String job = "未知";
public Builder(String name, int num, String address, long phone) {
this.name = name;
this.num = num;
this.address = address;
this.phone = phone;
}
public Builder sex(String sex) {
this.sex = sex;
return this;
}
public Builder hobby(String hobby) {
this.hobby = hobby;
return this;
}
public Builder job(String job) {
this.job = job;
return this;
}
public Order build() {
return new Order(this);
}
}
public void toPrint() {
System.out.println("Order{" +
"菜名='" + name + '\'' +
", 数量=" + num +
", 收货地址='" + address + '\'' +
", 收货人电话=" + phone +
", 收货人性别='" + sex + '\'' +
", 爱好='" + hobby + '\'' +
", 工作='" + job + '\'' +
'}');
}
public static void main(String[] args) {
Order order = new Order.Builder("北京烤鸭", 4, "西安钟楼", 18888888888L)
.hobby("爱打游戏").build();
order.toPrint();
}
}
输出:
Order{菜名='北京烤鸭', 数量=4, 收货地址='西安钟楼', 收货人电话=18888888888,
收货人性别='未知', 爱好='爱打游戏', 工作='未知'}