2017-03-08 51 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是非常好的。謝謝! –