JavaBuilder模式使用
最近,我看到一些开发人员使用嵌套的构建器类(如JavaBuilder模式使用,java,design-patterns,pojo,Java,Design Patterns,Pojo,最近,我看到一些开发人员使用嵌套的构建器类(如 public class User { private String firstName; private String lastName; public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = fir
public class User {
private String firstName;
private String lastName;
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public static class UserBuilder {
private String firstName;
private String lastName;
public User build() {
User user = new User();
user.firstName = firstName;
user.lastName = lastName;
return user;
}
public UserBuilder withFirstName(String firstName) {
this.firstName = firstName;
return this;
}
public UserBuilder withLastName(String lastName) {
this.firstName = firstName;
return this;
}
}
}
现在,他们声称这使代码更具可读性。我的观点是,这有以下缺点:
User user = new User.UserBuilder()
.withFirstName("Name")
.withLastName("surName")
.build();
这是约书亚·布洛赫的一篇文章。他很好地解释了为什么、何时以及如何使用建筑商: 这是他在《有效Java》一书中的一个项目。如果您对Java有一点经验,我强烈建议您阅读本书 要点: 当你得到一个有很多属性的类时,有几种方法可以创建一个对象并初始化它 如果你一个接一个地设置每一个属性,它可能会很冗长,并且你的对象在创建后可能会被改变。使用此方法,不可能使类不可变,也无法确保对象处于一致状态 文章中的示例:
public class NutritionFacts {
// Parameters initialized to default values (if any)
private int servingSize = -1; // Required; no default value
private int servings = -1; // " " " "
private int calories = 0;
private int fat = 0;
private int sodium = 0;
private int carbohydrate = 0;
public NutritionFacts() { }
// Setters
public void setServingSize(int val) { servingSize = val; }
public void setServings(int val) { servings = val; }
public void setCalories(int val) { calories = val; }
public void setFat(int val) { fat = val; }
public void setSodium(int val) { sodium = val; }
public void setCarbohydrate(int val) { carbohydrate = val; }
}
public class NutritionFacts {
private final int servingSize; // (mL) required
private final int servings; // (per container) required
private final int calories; // optional
private final int fat; // (g) optional
private final int sodium; // (mg) optional
private final int carbohydrate; // (g) optional
public NutritionFacts(int servingSize, int servings) {
this(servingSize, servings, 0);
}
public NutritionFacts(int servingSize, int servings,
int calories) {
this(servingSize, servings, calories, 0);
}
public NutritionFacts(int servingSize, int servings,
int calories, int fat) {
this(servingSize, servings, calories, fat, 0);
}
public NutritionFacts(int servingSize, int servings,
int calories, int fat, int sodium) {
this(servingSize, servings, calories, fat, sodium, 0);
}
public NutritionFacts(int servingSize, int servings,
int calories, int fat, int sodium, int carbohydrate) {
this.servingSize = servingSize;
this.servings = servings;
this.calories = calories;
this.fat = fat;
this.sodium = sodium;
this.carbohydrate = carbohydrate;
}
}
public class NutritionFacts {
private final int servingSize;
private final int servings;
private final int calories;
private final int fat;
private final int sodium;
private final int carbohydrate;
public static class Builder {
// Required parameters
private final int servingSize;
private final int servings;
// Optional parameters - initialized to default values
private int calories = 0;
private int fat = 0;
private int carbohydrate = 0;
private int sodium = 0;
public Builder(int servingSize, int servings) {
this.servingSize = servingSize;
this.servings = servings;
}
public Builder calories(int val)
{ calories = val; return this; }
public Builder fat(int val)
{ fat = val; return this; }
public Builder carbohydrate(int val)
{ carbohydrate = val; return this; }
public Builder sodium(int val)
{ sodium = val; return this; }
public NutritionFacts build() {
return new NutritionFacts(this);
}
}
private NutritionFacts(Builder builder) {
servingSize = builder.servingSize;
servings = builder.servings;
calories = builder.calories;
fat = builder.fat;
sodium = builder.sodium;
carbohydrate = builder.carbohydrate;
}
}
您可以使用伸缩构造函数。它可以使对象不可变。但是,如果您获得了许多属性,那么编写和读取代码可能会很困难。更详细地说,当您只想使用一个已设置的属性创建时,不幸的是,这是构造函数的最后一个参数,您必须设置所有参数
文章中的示例:
public class NutritionFacts {
// Parameters initialized to default values (if any)
private int servingSize = -1; // Required; no default value
private int servings = -1; // " " " "
private int calories = 0;
private int fat = 0;
private int sodium = 0;
private int carbohydrate = 0;
public NutritionFacts() { }
// Setters
public void setServingSize(int val) { servingSize = val; }
public void setServings(int val) { servings = val; }
public void setCalories(int val) { calories = val; }
public void setFat(int val) { fat = val; }
public void setSodium(int val) { sodium = val; }
public void setCarbohydrate(int val) { carbohydrate = val; }
}
public class NutritionFacts {
private final int servingSize; // (mL) required
private final int servings; // (per container) required
private final int calories; // optional
private final int fat; // (g) optional
private final int sodium; // (mg) optional
private final int carbohydrate; // (g) optional
public NutritionFacts(int servingSize, int servings) {
this(servingSize, servings, 0);
}
public NutritionFacts(int servingSize, int servings,
int calories) {
this(servingSize, servings, calories, 0);
}
public NutritionFacts(int servingSize, int servings,
int calories, int fat) {
this(servingSize, servings, calories, fat, 0);
}
public NutritionFacts(int servingSize, int servings,
int calories, int fat, int sodium) {
this(servingSize, servings, calories, fat, sodium, 0);
}
public NutritionFacts(int servingSize, int servings,
int calories, int fat, int sodium, int carbohydrate) {
this.servingSize = servingSize;
this.servings = servings;
this.calories = calories;
this.fat = fat;
this.sodium = sodium;
this.carbohydrate = carbohydrate;
}
}
public class NutritionFacts {
private final int servingSize;
private final int servings;
private final int calories;
private final int fat;
private final int sodium;
private final int carbohydrate;
public static class Builder {
// Required parameters
private final int servingSize;
private final int servings;
// Optional parameters - initialized to default values
private int calories = 0;
private int fat = 0;
private int carbohydrate = 0;
private int sodium = 0;
public Builder(int servingSize, int servings) {
this.servingSize = servingSize;
this.servings = servings;
}
public Builder calories(int val)
{ calories = val; return this; }
public Builder fat(int val)
{ fat = val; return this; }
public Builder carbohydrate(int val)
{ carbohydrate = val; return this; }
public Builder sodium(int val)
{ sodium = val; return this; }
public NutritionFacts build() {
return new NutritionFacts(this);
}
}
private NutritionFacts(Builder builder) {
servingSize = builder.servingSize;
servings = builder.servings;
calories = builder.calories;
fat = builder.fat;
sodium = builder.sodium;
carbohydrate = builder.carbohydrate;
}
}
生成器允许您的代码更可读、更易于编写。它还允许您使类不可变
文章中的示例:
public class NutritionFacts {
// Parameters initialized to default values (if any)
private int servingSize = -1; // Required; no default value
private int servings = -1; // " " " "
private int calories = 0;
private int fat = 0;
private int sodium = 0;
private int carbohydrate = 0;
public NutritionFacts() { }
// Setters
public void setServingSize(int val) { servingSize = val; }
public void setServings(int val) { servings = val; }
public void setCalories(int val) { calories = val; }
public void setFat(int val) { fat = val; }
public void setSodium(int val) { sodium = val; }
public void setCarbohydrate(int val) { carbohydrate = val; }
}
public class NutritionFacts {
private final int servingSize; // (mL) required
private final int servings; // (per container) required
private final int calories; // optional
private final int fat; // (g) optional
private final int sodium; // (mg) optional
private final int carbohydrate; // (g) optional
public NutritionFacts(int servingSize, int servings) {
this(servingSize, servings, 0);
}
public NutritionFacts(int servingSize, int servings,
int calories) {
this(servingSize, servings, calories, 0);
}
public NutritionFacts(int servingSize, int servings,
int calories, int fat) {
this(servingSize, servings, calories, fat, 0);
}
public NutritionFacts(int servingSize, int servings,
int calories, int fat, int sodium) {
this(servingSize, servings, calories, fat, sodium, 0);
}
public NutritionFacts(int servingSize, int servings,
int calories, int fat, int sodium, int carbohydrate) {
this.servingSize = servingSize;
this.servings = servings;
this.calories = calories;
this.fat = fat;
this.sodium = sodium;
this.carbohydrate = carbohydrate;
}
}
public class NutritionFacts {
private final int servingSize;
private final int servings;
private final int calories;
private final int fat;
private final int sodium;
private final int carbohydrate;
public static class Builder {
// Required parameters
private final int servingSize;
private final int servings;
// Optional parameters - initialized to default values
private int calories = 0;
private int fat = 0;
private int carbohydrate = 0;
private int sodium = 0;
public Builder(int servingSize, int servings) {
this.servingSize = servingSize;
this.servings = servings;
}
public Builder calories(int val)
{ calories = val; return this; }
public Builder fat(int val)
{ fat = val; return this; }
public Builder carbohydrate(int val)
{ carbohydrate = val; return this; }
public Builder sodium(int val)
{ sodium = val; return this; }
public NutritionFacts build() {
return new NutritionFacts(this);
}
}
private NutritionFacts(Builder builder) {
servingSize = builder.servingSize;
servings = builder.servings;
calories = builder.calories;
fat = builder.fat;
sodium = builder.sodium;
carbohydrate = builder.carbohydrate;
}
}
在您的示例中,我不确定为只有两个属性的类创建生成器是否非常有用
我希望这将对您有所帮助。在给定的示例中,使用构建器模式没有任何价值。您可以在不使用生成器的情况下创建
User
对象(因为有所有的setter)。在这种特殊情况下,Builder提供给您的唯一信息是
当存在创建有效对象的各种组合时,应该使用生成器模式。由于这一点,您不必实现许多构造函数或工厂方法
当创建需要许多参数的有效对象时,生成器也很有用
生成器应只负责生成有效的对象。在您的情况下,如果用户需要名和姓,则生成器不允许创建未设置这些属性的用户实例。从小型不可变对象开始
如果所有属性都是必需的,那么应该只使用构造函数。通过这样做,您可能会创建一个漂亮的不可变的小对象
当您有多个可选字段时,生成器非常有用
如果有多个可选字段和不同的方法来创建对象,则需要多个构造函数
public User (int requiredParameter) { ... }
public User (int reqiredParameter, int optionalParameter) { ... }
public User (int reqiredParameter, int optionalParameter, String optionalParameter2) { ... }
public User (int reqiredParameter, String optionalParameter2) { ... }
它会产生混乱的代码。很难确定应该使用哪个构造函数。
在这种情况下,您可以使用嵌套生成器以直观的方式创建对象 当您的构造函数有许多不同的可能性,并且其中许多类型相同时,您通常使用构建器。因此,构建器使一切都更具可读性和可重用性。构建器避免了“伸缩构造函数”反模式,在这种反模式中,所有可能的可选构造函数参数都会组合爆炸。有效Java第二版第2项中有详细讨论。这个链接提供了您应该知道的关于构建器模式使用的所有信息。我认为构建器对于构建不可变对象最有用,否则您必须将所有参数传递给构造函数。生成器模式使构造更具可读性和灵活性。但是在你的例子中,
用户
是可变的,所以你对构建器所能做的事情没有设置器所不能做的。Josh bloch解释了这个用例-请在你的答案中添加至少最重要的几点。完成了,我希望一切正常。