【架構設計】你的類足夠「專一」嗎

2022-12-26 06:01:11

前言

軟體設計SOLID原則中有一個最基礎的原則就是單一職責原則,我想絕大部分的程式設計師都知道,而且都理解它的意思,甚至覺得很簡單。但是往往「看懂」和「會用」是兩回事,而「用好」更是難上加難。好比我們專案,一開始一直和大家強調類的單一職責,隨著業務不斷髮展,不同的同事都往這個類「添磚加瓦」,最終導致一個類5000多行,如下圖所示,連IDE都受不了,變得卡頓,可想後面維護成本有多大了,究竟為什麼會這樣呢?又該如何解決呢?

歡迎關注微信公眾號「JAVA旭陽」交流和學習

什麼是單一職責原則(SRP)?

單一職責原則的英文是 Single Responsibility Principle,縮寫為 SRP。這個原則的英文描述是這樣的:A class or module should have a single reponsibility。如果我們把它翻譯成中文,那就是:一個類或者模組只負責完成一個職責(或者功能)。簡單來說,一個類只負責完成一個職責或者功能。也就是說,不要設計大而全的類,要設計粒度小、功能單一的類。

上例子,比如,一個類裡既包含訂單的一些操作,又包含使用者的一些操作。而訂單和使用者是兩個獨立的業務領域模型,我們將兩個不相干的功能放到同一個類中,那就違反了單一職責原則。為了滿足單一職責原則,我們需要將這個類拆分成兩個粒度更細、功能更加單一的兩個類:訂單類和使用者類。

如何判斷類是否夠「專一」?

聽起來類的單一職責很簡單,很容易區分清楚,就比如上面的例子中的使用者和訂單,那為什麼我們在開發過程中還是容易把一個類寫的越來越大呢?好吧我們專案中的這個5000多行的類,實際上都是和「指標」這個領域模型的相關操作,所以對於大部分同事很難去界定出來哪些功能歸為一類,屬於一個職責範圍之內的,既然搞不清楚,那麼我就都寫一起好了,大部分都是這樣去寫程式碼的。

這邊再用一個實際的例子解釋下這種模糊的情況。

比如下面的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 類包含的資訊都是使用者相關的行為,滿足單一職責原則。
  • 有人認為,地址資訊在 UserInfo 類中,所佔的比重比較高,可以繼續拆分成獨立的 UserAddress 類,UserInfo 只保留除 Address 之外的其他資訊,拆分之後的兩個類的職責更加單一。

其實是否滿足單一職責原則,需要結合具體的業務場景,如果你現在是社交場景中,地址單純用來展示使用,那麼它就是符合單一職責原則。如果在電商場景中,地址就要訂單、物流等資訊中出現,那麼就要做進一步拆分。

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

小結:

評價一個類的職責是否"專一",我們並沒有一個非常明確的、可以量化的標準,可以說,這是件非常主觀、仁者見仁智者見智的事情。實際上,在真正的軟體開發中,我們也沒必要過於未雨綢繆,過度設計。所以,我們可以先寫一個粗粒度的類,滿足業務需求。隨著業務的發展,如果粗粒度的類越來越龐大,程式碼越來越多,這個時候,我們就可以將這個粗粒度的類,拆分成幾個更細粒度的類,進行持續重構

該具體怎麼做?

上面判定一個類是否具備單一職責,還是比較模糊、主觀,那麼有沒有具體地、可執行的標準來判斷呢?比如以我們的專案為例:

  • 最關鍵的一條,如果類中的程式碼行數、函數或屬性過多,影響到程式碼的可讀性和可維護性,就需要考慮對類進行拆分。我們專案後面要求程式碼不能超過1500行,函數最好不要超過20個。
  • 私有方法過多,我們就要考慮能否將私有方法獨立到新的類中,設定為 public 方法,供更多的類使用,從而提高程式碼的複用性;
  • 類中大量的方法都是集中操作類中的某幾個屬性,比如,在 UserInfo 例子中,如果一半的方法都是在操作 address 資訊,那就可以考慮將這幾個屬性和對應的方法拆分出來。
  • 另外一個很關鍵的一點是,在為了一個加方法、屬性的時候,一定要想清楚這是不是這個屬於這個類職責,不清楚,就問問團隊其他人,慢慢培養自己的「專業感」,千萬不要有無腦地放哪裡都行、實現功能就行的心態。

總結

本文探討了類的單一職責原則,如何判定一個類是否"專一",以及具體該如何做。不僅僅是類,我們可以進行引申,你的方法足夠單一嗎?你的模組或者服務足夠單一嗎?另外,很關鍵的一點就是你在寫程式碼的時候,一定要動腦子,思考這段程式碼寫這裡合適嗎?,為這段程式碼找到一個真正屬於它的「家」。多多思考,那麼你一定會變得越來越專業。

歡迎關注微信公眾號「JAVA旭陽」交流和學習
更多學習資料請移步:程式設計師成神之路