2017-03-08 49 views
1

考虑下面的类:Builder模式和failling

@Getter 
public class EmailVO { 
    private final Long id; 
    private final String firstName; 
    private final String email; 
    private final String address; 

    @Slf4j 
    @Component 
    @Scope("prototype") 
    public static class Builder { 
     private Lead lead; 

     private Long id; 
     private String firstName; 
     private String email; 
     private String address; 

     public Builder fromLead(Lead lead) { 
      this.lead = lead; 
      return this; 
     } 

     public EmailVO build() { 
      if (lead == null) { 
       log.error("Failed to build EmailVO: lead was not initialized"); 
       return new EmailVO(this); 
      } 

      User user = lead.getUser(); 
      id = user.getId(); 
      firstName = user.getFirstName(); 
      email = user.getEmail(); 
      address = user.getAddress(); 

      return new EmailVO(this); 
     } 
    } 

    private EmailVO(Builder builder) { 
     id = builder.id; 
     firstName = builder.firstName; 
     email = builder.email; 
     address = builder.address; 

     if (id == null || 
      firstName == null || 
      email == null || 
      address == null) 
     { 
      throw new IllegalStateException(); // Maybe some ohter Unchecked Exception would be better 
     } 
    } 
} 

据我所知,这将是一个正确的VO类实现只允许建立新的实例,从它的建设者,这也遵循充分的建设者模式(纠正我,如果我错了)。

从SOLID的角度来看,这个代码没有问题,因为构建器的单一职责是汇总数据以构建EmailVO,构造函数将负责让它的有效实例生效。现在,如果您关心代码混乱和可读性(想象一个更大的VO),当构建器尝试构建而不需要参数初始化时,构建器可能会失败,而不是让对象的构造器失败,从而可能删除构造函数内部有很多空的检查。在示例代码,如果leadnull这个验证可能抛出一个异常,而不是让EmailVO的构造尽管是构造有责任检查其完整性。

EmailVO的构造函数中删除验证并让建造者照顾它会好吗?考虑到在这种情况下,构造函数是private,这使得它在课堂外不可见。

这看起来像是违背了SOLID,尽管如果构建器没有验证所需的参数,它可能会失败,它的一个责任是汇总所需的数据来构建一个EmailVO实例。

然而,一个想法掠过我脑海的是有一个标志为EmailVO.Builder类的成员字段以突出它是否在聚合所需的参数成功与否,然后EmailVO的构造,只能查看(和信任)这个标志。

+0

考虑到你的'EmailVO'严格控制变量,我会说它应该有零逻辑。 –

+1

在你的EmailVO构造函数中,考虑替换'x = builder.x; if(x == null){}'with'x = Objects.requireNonNull(builder.x);' – toongeorges

+0

@ChristopherSchneider好点。 VO对象应该仅用于传递值,而不是用于验证它们,但是如果让构建者执行此类验证,它不会违反SOLID吗?人们可以说,你可以让所有需要的验证发生在一个孤立的方法中(在Controller内部),但是这将允许构建器构建部分构建的对象,这是否违背构建器模式? –

回答

1

考虑到你的EmailVO是严格保存数值, 我建议你尽量减少setter和构造器(该@Christopher施耐德评论的变化)的登录。

如果您需要验证, 您可以将验证方法添加到EmailVO对象。

关于“建立与坏的价值观”问题,即验证添加到建设者。

EmailVO验证可能包括像“这是一个有效地构建电子邮件地址”。

而且,建设者不传递到EmailVO对象,只是所使用的建设者包接入构造。确保建造者与EmailVO对象位于同一个包中。

+0

_如果你想要验证,你可以为EmailVO对象添加一个验证方法。当然,当我说验证时,我的意思是检查是否存在需要的参数,而不是语义和一致性验证。但是关于_关于“构建不好的值”问题,请将该验证添加到构建器中._这正是我想要做的,但是这不是针对SOLID的吗?这是我不确定的。 –

+1

我看不到建造者会破坏SOLID。具体而言,构建者的责任是构建一个有效的'EmailVO'。这意味着构建者负责防止构造不好的'EmailVO'对象。 – DwB

+0

建造者是否负责防止坏物的构造?它不是负责聚合对象构造的参数吗?正如你所说,我正在阅读它:_A构建器负责汇总用于构建对象的参数**和**,以防止构造此对象的畸形实例__ –

2

看起来你正在使用Project Lombok到eleminate很多的样板。也许immmutables将是一个不错的选择。

Immutables在编译时通过注释处理器创建样板,并且有一个方便的方式来创建一个流利的构建器pojo。

从着陆页:

// Define an abstract value type 
@Value.Immutable 
public interface ValueObject { 
    String name(); 
    List<Integer> counts(); 
    Optional<String> description(); 
} 

// Then use generated immutable implementation 
ValueObject valueObject = 
    ImmutableValueObject.builder() 
     .name("My value") 
     .addCounts(1) 
     .addCounts(2) 
     .build(); 
+0

这确实消除了我的问题,但我仍然想知道在这种情况下对SOLID最好做什么。无论如何,了解Immutables是非常好的。谢谢! –