天天看點

設計原則:單一職責原則

單一職責原則(SRP)

單一職責原則的英文是 Single Responsibility Principle,縮寫為 SRP。這個原則的英文描述是這樣的:A class or module should have a single reponsibility。翻譯成中文,那就是:一個類或者子產品隻負責完成一個職責(或者功能)。

單一職責原則的定義描述非常簡單:一個類隻負責完成一個職責或者功能。也就是說,不要設計大而全的類,要設計粒度小、功能單一的類。換個角度來講就是,一個類包含了兩個或者兩個以上業務不相幹的功能,那我們就說它職責不夠單一,應該将它拆分成多個功能更加單一、粒度更細的類。

如何判斷類的職責是否單一

其實,評價一個類的職責是否足夠單一,我們并沒有一個非常明确的、可以量化的标準,可以說,這是件非常主觀、仁者見仁智者見智的事情。實際上,在真正的軟體開發中,我們也沒必要過于未雨綢缪,過度設計。是以,我們可以先寫一個粗粒度的類,滿足業務需求。随着業務的發展,如果粗粒度的類越來越龐大,代碼越來越多,這個時候,我們就可以将這個粗粒度的類,拆分成幾個更細粒度的類。這就是所謂的持續重構。

在不同的應用場景、不同的業務層面、不同階段的需求背景下,對同一個類的職責是否單一的判定,可能都是不一樣的。在某種應用場景或者當下的需求背景下,一個類的設計可能已經滿足單一職責原則了,但如果換個應用場景或着在未來的某個需求背景下,可能就不滿足了,需要繼續拆分成粒度更細的類。

比如,在一個社交産品中,我們用下面的 UserInfo 類來記錄使用者的資訊。

public class UserInfo {
  private Long userId;
  private String username;
  private String email;
  private String avatarUrl;
  // 省
  private String provinceAddress;
  // 市
  private String cityAddress;
  // 區
  private String regionAddress;
  // 詳細位址
  private String detailAddress;
}
           

對于 UserInfo 類的設計,如果它裡面包含的資訊隻是單純地用來展示,像我們的抖音号一樣,隻是顯示資訊,那麼 Userlnfo 現在的設計是滿足單一職責原則的。但是,如果這個社交産品後來又添加了電商的子產品,使用者的位址資訊還會用在電商物流中,此時的 UserInfo 類的設計就不滿足單一職責原則,那麼我們最好将位址資訊從 Userlnfo 中拆分出來,獨立成專門的使用者位址資訊。

那麼在實際開發中,我們該如何判斷一個類是否職責單一呢?這裡,我們可以從一些側面的名額判斷一個類是否足夠單一,比如,出現下面這些情況就有可能說明類的設計不滿足單一職責原則:

  • 類中的代碼行數、函數或屬性過多,會影響代碼的可讀性和可維護性,我們就需要考慮對類進行拆分;
  • 類依賴的其他類過多,或者依賴類的其他類過多,不符合高内聚、低耦合的設計思想,我們就需要考慮對類進行拆分;
  • 私有方法過多,我們就要考慮能否将私有方法獨立到新的類中,設定為 public 方法,供更多的類使用,進而提高代碼的複用性;
  • 比較難給類起一個合适名字,很難用一個業務名詞概括,或者隻能用一些籠統的 Manager Context 之類的詞語來命名,這就說明類的職責定義得可能不夠清晰;
  • 類中大量的方法都是集中操作類中的某幾個屬性,那就可以考慮将這幾個屬性和對應的方法拆分出來。

類的職責是否設計得越單一越好

單一職責原則通過避免設計大而全的類,避免将不相關的功能耦合在一起,來提高類的内聚性。同時,類職責單一,類依賴的和被依賴的其他類也會變少,減少了代碼的耦合性,以此來實作代碼的高内聚、低耦合。但是,如果拆分得過細,實際上會适得其反,反而會降低内聚性,也會影響代碼的可維護性。是以,在設計類的時候,滿足目前需求即可,不應過度設計。

繼續閱讀