生成器模式对Setter方法有用吗?

时间:2015-10-20 07:10:48

标签: java testing design-patterns builder builder-pattern

所以我有一个关于Hybris,Spring等的webproject。

我有一些自动生成的类。假设我有一个模型类,它是自动生成的并且从另一个类继承了一些方法来设置字段。

编写单元测试时,开始使用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();

但是因为我已经有了Setter-Methods,所以我通常会:

Employee emp = new Employee();
emp.setId(123);
emp.setName("John");

在这种情况下,真的值得努力吗?或者有什么我还没有真正理解的东西?

谢谢!

4 个答案:

答案 0 :(得分:4)

构建器模式对于:

非常有用
  • 不可变类,这不是这种情况。
  • 当你需要构建许多相同的东西时,会有细微差别。这也不是这种情况。
  • 编写“流动”API。
  • 当你有一个需要复杂构建的复杂对象时。
  

在这种情况下,真的值得努力吗?

鉴于你发布了什么,我会说不。

最后,使用正确的API(例如Project LombokGoogle Auto)所涉及的工作量很小。 (另外如果你使用构建器来隐藏伸缩式构造函数反模式,我认为你正在滥用模式,但是嘿......)

答案 1 :(得分:3)

在我回答你的问题之前,我想解释一下建造者模式。

当你有很多重载的构造函数(telescoping constructor anti-pattern)时,通常会使用构建器模式。 E.g。

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) {
       ...
   }
}

在这种情况下,客户端代码必须根据其拥有的数据决定调用哪个构造函数。如果它有firstNamelastName,则必须调用new Employee(firstName, lastName)。如果它只有firstName,则必须调用Employee(String firstName)。所以客户端代码可能有很多if / then / else。 E.g。

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类的设计包括firstNamelastNameEmployee的必需属性,因为每个构造函数都需要它们。属性sexsaluation是可选的。如果客户端代码决定调用哪个构造函数也意味着决策过程在客户端代码中重复。例如。如果客户端知道firstNamelastNamesexsalutation应该调用哪个构造函数? 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);
          }
      }
}

这使得客户端代码更容易阅读,并且构造函数调用决策被封装。 E.g。

EmployeeBuidler employeeBuilder = new EmployeeBuilder(firstName, lastName);

Sex sex = ...; 
String salutation = ...;

employeeBuilder.setSex(sex);
employeeBuilder.setSalutation(salutation);

Employee employee = employeeBuilder.build();

回到你的问题

  

在这种情况下,真的值得努力吗?

对于您的单元测试,您可能希望创建具有某些属性的Employee个对象,而其他对象应设置为默认值。在这种情况下,我认为使用构建器模式是个好主意。我会命名建造者,例如EmployeeDefaultValuesBuilder明确说明。

您可能还希望根据其他员工对象(模板)构建Employee。在这种情况下,我会向EmployeeBuilder添加另一个构造函数。 E.g。

public EmployeeBuilder(Employee template){
  // initialize this builder with the values of the template
}

因此,如果您封装构造逻辑或增加可读性,那么值得付出努力。

答案 2 :(得分:2)

Builder模式在两种情况下很有用:

  • 结果对象是不可变的(所有字段都是final) - 构建器是 比具有许多参数的构造函数更好。
  • 您希望确保创建的对象有效且不能创建任何不一致的对象 - 例如,如果设置了longitude字段但未latitude字段,则可以从build()方法中抛出错误。

答案 3 :(得分:1)

正如您在代码示例中演示的那样,使用构建器模式,您只需节省一些时间来写出变量名emp,但还必须添加最终的build()调用或类似内容。

在我的书中,肯定不能支付创建额外建筑商的投资。

但是......

也许您有需要填写的字段,但这与您想要测试的内容无关。

或者您想要创建仅在少数属性中不同的多个实例。

这些可以在构建器中很好地构建,节省了大量代码,更重要的是,使您的测试更加清晰。