use*_*542 4 java testing design-patterns builder builder-pattern
所以我有一个带有 Hybris、Spring 等的 web 项目。
我有一些类,它们是自动生成的。假设我有一个模型类,它是自动生成的并从另一个类继承一些方法来设置字段。
在编写单元测试时,开始使用 Builder 模式有用吗?因为问题是,我没有构造函数,比如 Employee(int id, String name) 等等,我只有继承的方法来设置它们(setId(int id) 等等)。
例如,当我为这个模型编写一个 Builder 类时,我会使用 .withId(int id) 和 .withName(String name) 以及 build() 方法,我将在其中运行 setter 方法。
所以最后在我的测试课上我会有:
EmployeeBuilder eb = new EmployeeBuilder();
Employee emp = eb.withId(123)
.withName("John")
.build();
Run Code Online (Sandbox Code Playgroud)
但是因为我已经有了 Setter-Methods 我通常有:
Employee emp = new Employee();
emp.setId(123);
emp.setName("John");
Run Code Online (Sandbox Code Playgroud)
那么在这种情况下真的值得付出努力吗?还是有什么我没有真正理解的地方?
谢谢!
在我回答你的问题之前,我想解释一下构建器模式。
当你有很多重载的构造器(伸缩构造器反模式)时,通常会使用构建器模式。例如
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) {
...
}
}
Run Code Online (Sandbox Code Playgroud)
在这种情况下,客户端代码必须根据它拥有的数据决定调用哪个构造函数。如果它有 afirstName
并且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 {
.....
}
Run Code Online (Sandbox Code Playgroud)
Employee
本示例中类的设计包括firstName
和lastName
是 an 的必需属性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);
}
}
}
Run Code Online (Sandbox Code Playgroud)
这使得客户端代码更易于阅读,并且封装了构造函数调用决策。例如
EmployeeBuidler employeeBuilder = new EmployeeBuilder(firstName, lastName);
Sex sex = ...;
String salutation = ...;
employeeBuilder.setSex(sex);
employeeBuilder.setSalutation(salutation);
Employee employee = employeeBuilder.build();
Run Code Online (Sandbox Code Playgroud)
回到你的问题
那么在这种情况下真的值得付出努力吗?
对于您的单元测试,您可能希望创建Employee
具有某些属性的对象,而其他属性应设置为默认值。在这种情况下,我认为使用构建器模式是个好主意。我会命名构建器然后例如EmployeeDefaultValuesBuilder
以使其清楚。
您可能还想Employee
根据其他员工对象(模板)构建s。在这种情况下,我会向EmployeeBuilder
. 例如
public EmployeeBuilder(Employee template){
// initialize this builder with the values of the template
}
Run Code Online (Sandbox Code Playgroud)
因此,如果您封装构造逻辑或增加可读性,那么付出努力是值得的。
构建器模式适用于:
那么在这种情况下真的值得付出努力吗?
鉴于您发布的内容,我会说不。
最后,当使用正确的 API(例如Project Lombok或Google Auto)时,所涉及的工作量很小。(另外,如果您使用构建器来隐藏伸缩构造函数反模式,我认为您正在滥用该模式,但是嘿......)