史無前例的設計模式-單一職責原則

一、概念

單一職責:Single Responsibility Principle,簡稱SRP。字面義很簡單,但是做起來有的時候真的蠻難的。就是小到一個接口只幹一件事,大到一個類、一個模塊只負責完成一個功能。
比如:UserService裏面既包含用戶賬號密碼登錄註冊等操作又包含用戶積分等操作。這種情況就很明顯的違背了單一職責原則,需要將其拆分爲UserService和ScoreService。

二、如何確定是否單一職責

1、描述

上面那麼明顯的demo就不多說了,但是一般都會出現摸棱兩可的情況,比如下面的UserInfo,這就需要憑藉個人經驗和實際業務需求來評估了。


public class UserInfo {
  private long userId;
  private String username;
  private String email;
  private String telephone;
  private long createTime;
  private long lastLoginTime;
  private String avatarUrl;
  private String provinceOfAddress; // 省
  private String cityOfAddress; // 市
  private String regionOfAddress; // 區 
  private String detailedAddress; // 詳細地址
  // ...省略其他屬性和方法...
}

這段代碼符合單一職責嗎?有的人說符合,有的人說不符合,應該把省市區詳細地址單獨抽出來放到一個類裏。
哪種人說的對?我覺得這需要區分業務,如果你的產品業務的用戶信息字段只是負責顯示,沒其他邏輯。那這一個UserInfo.java就足夠了,如果你不光有用戶系統,還有積分兌換系統,兌換的實物需要發到你的省市區詳細地址,這種情況我認爲UserInfo.java結合UserAddress.java兩個類比較合適。因爲地址信息不光顯示用,物流發貨也需要用。
所以我覺得,這就是取決於產品業務的,當然特別明顯的違背了單一職責原則的代碼一眼就看得出,只有這種摸棱兩可的代碼需要取決於業務。但我覺得沒必要過度設計,如果你係統有這個需求或者近期可能會出現這個需求,那就拆分。如果近期不會這麼搞,我覺得先弄一個類,到時候重構就完了,不要過度設計。

2、總結

總結評判是否符合單一職責原則的幾個技巧

  • 類的代碼行數、函數、屬性過多的時候。多少算多?個人建議類行數不過300行、函數不過50行、函數和屬性的個人不多於10個。
  • 類依賴的其他類過多,不符合高內聚低耦合的思想,需要拆分。比如一個service層又依賴了其他七八個service層才能完成某個業務。
  • 私有方法過多,可以考慮放到其他類里弄成public公用。提高複用性。
  • 根據業務場景來適當拆分,這句話好像是廢話…這裏想說的是那個UserInfo和UserAddress的取捨。

三、切勿過度設計

千萬不要爲了單一而過度單一。比如Serialization實現了序列化和反序列化的功能。

public class Serialization {
  private static final String IDENTIFIER_STRING = "CTW";
  private Gson gson;
  
  public Serialization() {
    this.gson = new Gson();
  }
  /**
   * 序列化
   */
  public String serialize(Map<String, String> object) {
    StringBuilder textBuilder = new StringBuilder();
    textBuilder.append(IDENTIFIER_STRING);
    textBuilder.append(gson.toJson(object));
    return textBuilder.toString();
  }
  
  /**
   * 反序列化
   */
  public Map<String, String> deserialize(String text) {
    if (!text.startsWith(IDENTIFIER_STRING)) {
        return Collections.emptyMap();
    }
    String gsonStr = text.substring(IDENTIFIER_STRING.length());
    return gson.fromJson(gsonStr, Map.class);
  }
}

上面的設計很合理,也很完美,完全符合上面【如何確定是否單一職責】的描述。但是如下代碼就過度設計了,他將序列化和反序列化拆分成兩個Java類。

/**
 * 序列化
 */
public class Serializer {
  private static final String IDENTIFIER_STRING = "CTW";
  private Gson gson;
  
  public Serializer() {
    this.gson = new Gson();
  }
  
  public String serialize(Map<String, String> object) {
    StringBuilder textBuilder = new StringBuilder();
    textBuilder.append(IDENTIFIER_STRING);
    textBuilder.append(gson.toJson(object));
    return textBuilder.toString();
  }
}

/**
 * 反序列化
 */
public class Deserializer {
  private static final String IDENTIFIER_STRING = "CTW";
  private Gson gson;
  
  public Deserializer() {
    this.gson = new Gson();
  }
  
  public Map<String, String> deserialize(String text) {
    if (!text.startsWith(IDENTIFIER_STRING)) {
        return Collections.emptyMap();
    }
    String gsonStr = text.substring(IDENTIFIER_STRING.length());
    return gson.fromJson(gsonStr, Map.class);
  }
}

乍一看好像是更加符合單一職責了,其實是典型的過度設計。首先完全沒必要,照這個思路的話,那所有的類裏面都只會包含一個方法了,其次他會引入新的問題,若我們修改了協議,數據標識從CTW變成了HELLO,或者序列化的方式從JSON換成了XML等,那Serializer和Deserializer兩個類都需要做相應的修改。顯然沒有第一種方式的內聚性高,主要是我們如果只修改了Serializer的CTW->HELLO,忘記了反序列化的類的修改,這時候會導致序列化、反序列化不一致,報錯。(我知道你能將CTW放到公用常量類裏去解決,我只想突出問題,說明這是過度設計)。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章