前言
軟體設計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旭陽」交流和學習
更多學習資料請移步:程式設計師成神之路