构建器模式对 Setter 方法有用吗?
Builder Pattern useful with Setter-methods?
所以我有一个带有 Hybris 的网络项目,Spring 等等。
我有一些 classes,它们是自动生成的。假设我有一个模型class,它是自动生成的并继承自另一个class 一些方法,用于设置字段。
编写单元测试时,开始使用构建器模式有用吗?因为问题是,我没有构造函数,比如 Employee(int id, String name) 等等,我只有继承的方法来设置它们(setId(int id) 等等)。
因此,当我为这个模型编写一个构建器 class 时,我会有方法 .withId(int id) 和 .withName(String name) 以及 build() 方法,其中我会 运行 setter 方法。
所以最后在我的测试中-class我会:
EmployeeBuilder eb = new EmployeeBuilder();
Employee emp = eb.withId(123)
.withName("John")
.build();
但是因为我已经有了Setter-我通常有的方法:
Employee emp = new Employee();
emp.setId(123);
emp.setName("John");
那么在这种情况下真的值得付出努力吗?或者有什么我还没有真正理解的东西?
谢谢!
正如您在代码示例中所演示的那样,使用构建器模式,您只需节省写出变量名 emp
的时间,但还必须添加最终的 build()
调用或类似调用。
在我看来,这肯定不会收回创建额外构建器的投资。
但是...
也许您有需要填写的字段,但这些字段与您要测试的内容并不相关。
或者您想要创建仅在少数属性上有所不同的多个实例。
这些可以在构建器中很好地构建,节省大量代码行,更重要的是,使您的测试更加清晰。
在回答您的问题之前,我想解释一下构建器模式。
当你有很多重载的构造函数时,通常使用构建器模式(伸缩构造函数反模式)。例如
public class Employee {
public Employee(String firstName, String lastName){
...
}
public Employee(String firstName, String lastName, Sex sex){
...
}
public Employee(String firstName, String lastName, String salutation) {
...
}
}
在这种情况下,客户端代码必须根据它拥有的数据来决定调用哪个构造函数。如果它有 firstName
和 lastName
,它必须调用 new Employee(firstName, lastName)
。如果它只有 firstName
,它必须调用 Employee(String firstName)
。所以客户端代码可能有很多if/then/else。例如
Employee employee = null;
if(firstName != null && lastName != null && sex != null){
employee = new Employee(firstName, lastName, sex);
} else if(firstName != null && lastName != null && salutation != null){
employee = new Employee(firstName, lastName, salutation );
} else {
.....
}
本例中Employee
class的设计包括firstName
和lastName
是Employee
的强制属性,因为每个构造函数都需要他们。属性 sex
和 saluation
是可选的。如果客户端代码决定调用哪个构造函数,这也意味着决策过程在客户端代码中是重复的。例如。如果客户端知道 firstName
、lastName
、sex
和 salutation
应该调用哪个构造函数? new Employee(firstName, lastName, sex)
或 new Employee(firstName, lastName, saluation)
?
为了封装构造函数解析,您可能需要使用构建器模式。
public class EmployeeBuilder {
public EmployeeBuilder(String firstName, String lastName){
}
public void setSex(Sex sex){ ... }
public void setSalutation(Salutation salutation){ ... }
public Employee build(){
if(salutation != null){
return new Emplyoee(firstName, lastName, salutation);
} else if(sex != null){
return new Emplyoee(firstName, lastName, sex);
} else {
return new Emplyoee(firstName, lastName);
}
}
}
这使得客户端代码更易于阅读,并且封装了构造函数调用决策。例如
EmployeeBuidler employeeBuilder = new EmployeeBuilder(firstName, lastName);
Sex sex = ...;
String salutation = ...;
employeeBuilder.setSex(sex);
employeeBuilder.setSalutation(salutation);
Employee employee = employeeBuilder.build();
回到你的问题
So is it really worth the effort in this case?
对于您的单元测试,您可能希望创建 Employee
个具有某些属性的对象,而其他属性应设置为默认值。在这种情况下,我认为使用构建器模式是个好主意。然后我会给建造者命名,例如EmployeeDefaultValuesBuilder
说清楚。
您可能还想根据其他员工对象(模板)构建 Employee
s。在这种情况下,我将向 EmployeeBuilder
添加另一个构造函数。例如
public EmployeeBuilder(Employee template){
// initialize this builder with the values of the template
}
因此,如果封装构造逻辑或增加可读性,那么值得付出努力。
建造者模式在两种情况下很有用:
- 结果对象是不可变的(所有字段都是
final
)- 生成器是
比有很多参数的构造函数更好。
- 您想确保创建的对象有效并且不会创建不一致的对象 - 例如,如果设置了
longitude
字段但设置了 latitude
,您可以从 build() 方法中抛出错误没有。
构建器模式适用于:
- 不可变类,这里不是这样。
- 当您需要构建许多相同但略有不同的东西时。这里也不是这种情况。
- 写 "Fluent" API.
- 当您有一个需要复杂构建的复杂对象时。
So is it really worth the effort in this case?
鉴于您发布的内容,我会拒绝。
最后,当使用正确的 API 时,所涉及的工作量很小,例如 Project Lombok or Google Auto。 (此外,如果您使用构建器来隐藏伸缩构造函数反模式,我认为您在滥用该模式,但是嘿...)
所以我有一个带有 Hybris 的网络项目,Spring 等等。
我有一些 classes,它们是自动生成的。假设我有一个模型class,它是自动生成的并继承自另一个class 一些方法,用于设置字段。
编写单元测试时,开始使用构建器模式有用吗?因为问题是,我没有构造函数,比如 Employee(int id, String name) 等等,我只有继承的方法来设置它们(setId(int id) 等等)。
因此,当我为这个模型编写一个构建器 class 时,我会有方法 .withId(int id) 和 .withName(String name) 以及 build() 方法,其中我会 运行 setter 方法。
所以最后在我的测试中-class我会:
EmployeeBuilder eb = new EmployeeBuilder();
Employee emp = eb.withId(123)
.withName("John")
.build();
但是因为我已经有了Setter-我通常有的方法:
Employee emp = new Employee();
emp.setId(123);
emp.setName("John");
那么在这种情况下真的值得付出努力吗?或者有什么我还没有真正理解的东西?
谢谢!
正如您在代码示例中所演示的那样,使用构建器模式,您只需节省写出变量名 emp
的时间,但还必须添加最终的 build()
调用或类似调用。
在我看来,这肯定不会收回创建额外构建器的投资。
但是...
也许您有需要填写的字段,但这些字段与您要测试的内容并不相关。
或者您想要创建仅在少数属性上有所不同的多个实例。
这些可以在构建器中很好地构建,节省大量代码行,更重要的是,使您的测试更加清晰。
在回答您的问题之前,我想解释一下构建器模式。
当你有很多重载的构造函数时,通常使用构建器模式(伸缩构造函数反模式)。例如
public class Employee {
public Employee(String firstName, String lastName){
...
}
public Employee(String firstName, String lastName, Sex sex){
...
}
public Employee(String firstName, String lastName, String salutation) {
...
}
}
在这种情况下,客户端代码必须根据它拥有的数据来决定调用哪个构造函数。如果它有 firstName
和 lastName
,它必须调用 new Employee(firstName, lastName)
。如果它只有 firstName
,它必须调用 Employee(String firstName)
。所以客户端代码可能有很多if/then/else。例如
Employee employee = null;
if(firstName != null && lastName != null && sex != null){
employee = new Employee(firstName, lastName, sex);
} else if(firstName != null && lastName != null && salutation != null){
employee = new Employee(firstName, lastName, salutation );
} else {
.....
}
本例中Employee
class的设计包括firstName
和lastName
是Employee
的强制属性,因为每个构造函数都需要他们。属性 sex
和 saluation
是可选的。如果客户端代码决定调用哪个构造函数,这也意味着决策过程在客户端代码中是重复的。例如。如果客户端知道 firstName
、lastName
、sex
和 salutation
应该调用哪个构造函数? new Employee(firstName, lastName, sex)
或 new Employee(firstName, lastName, saluation)
?
为了封装构造函数解析,您可能需要使用构建器模式。
public class EmployeeBuilder {
public EmployeeBuilder(String firstName, String lastName){
}
public void setSex(Sex sex){ ... }
public void setSalutation(Salutation salutation){ ... }
public Employee build(){
if(salutation != null){
return new Emplyoee(firstName, lastName, salutation);
} else if(sex != null){
return new Emplyoee(firstName, lastName, sex);
} else {
return new Emplyoee(firstName, lastName);
}
}
}
这使得客户端代码更易于阅读,并且封装了构造函数调用决策。例如
EmployeeBuidler employeeBuilder = new EmployeeBuilder(firstName, lastName);
Sex sex = ...;
String salutation = ...;
employeeBuilder.setSex(sex);
employeeBuilder.setSalutation(salutation);
Employee employee = employeeBuilder.build();
回到你的问题
So is it really worth the effort in this case?
对于您的单元测试,您可能希望创建 Employee
个具有某些属性的对象,而其他属性应设置为默认值。在这种情况下,我认为使用构建器模式是个好主意。然后我会给建造者命名,例如EmployeeDefaultValuesBuilder
说清楚。
您可能还想根据其他员工对象(模板)构建 Employee
s。在这种情况下,我将向 EmployeeBuilder
添加另一个构造函数。例如
public EmployeeBuilder(Employee template){
// initialize this builder with the values of the template
}
因此,如果封装构造逻辑或增加可读性,那么值得付出努力。
建造者模式在两种情况下很有用:
- 结果对象是不可变的(所有字段都是
final
)- 生成器是 比有很多参数的构造函数更好。 - 您想确保创建的对象有效并且不会创建不一致的对象 - 例如,如果设置了
longitude
字段但设置了latitude
,您可以从 build() 方法中抛出错误没有。
构建器模式适用于:
- 不可变类,这里不是这样。
- 当您需要构建许多相同但略有不同的东西时。这里也不是这种情况。
- 写 "Fluent" API.
- 当您有一个需要复杂构建的复杂对象时。
So is it really worth the effort in this case?
鉴于您发布的内容,我会拒绝。
最后,当使用正确的 API 时,所涉及的工作量很小,例如 Project Lombok or Google Auto。 (此外,如果您使用构建器来隐藏伸缩构造函数反模式,我认为您在滥用该模式,但是嘿...)