程序員過關斬將--領導說我的類的職責不單一
“為什么類的職責要單一化?
“類的職責單一化很容易嗎?
首先,我要提醒一下看到這篇文章的同學,我認為保證類(一定是類嗎?)的單一職責并不容易
軟件開發過程中,自古就流傳著幾大規則,無論如何這里都要和大家闡述一遍
單一職責原則
一個類應該只有一個發生變化的原因
開閉原則
軟件實體應該是可擴展,而不可修改的。也就是說,對擴展是開放的,而對修改是封閉的。這個原則是諸多面向對象編程原則中最抽象、最難理解的一個。
里氏替換原則
所有引用基類的地方必須能透明地使用其子類的對象,換句話說,子類在任何引用基類的地方都可以替換成子類。
依賴倒置原則
這個原則說的詳細一點其實可以概括為兩點:
高層模塊不應該直接依賴于底層模塊,應該依賴于抽象
抽象不應該依賴于具體實現,具體實現應該依賴于抽象
接口隔離原則
程序不依賴于不使用的接口,換句話說,一個程序只依賴于它需要的接口。
單純從概念上講呢,單一職責原則可算是最簡單易懂的一種原則了,就像設計模式中的單例模式一樣無趣,是這樣嗎?
誰的職責
說實話,看過不少講解“職責單一”設計原則的文章,都是以類來闡述。其實我覺得不對,職責單一設計原則本質上是軟件設計原則的一種思想,具有指導意義。至于誰的職責需要單一,是一個偽命題,不僅僅指面向對象編程中的類,系統的模塊,甚至于微服務在架構設計中也應該遵循此規則。
在面向對象設計的理解中,程序最基本的組成單位是類(class),多個類組成模塊(module),多個模塊組成服務(service),多個服務組成系統(system),一般的軟件系統都會存在以上幾個概念。
無論是類,還是模塊,還是服務,還是系統,我認為設計的時候都要保證“單一職責”。
單一真的容易嗎
說到“單一”職責,每個人都有不同的看法
- class UserInfo
- {
- //用戶id
- public int UserId{get ;set ;}
- //用戶登錄賬號
- public string Account{get;set ;}
- //用戶登錄密碼
- public string Pwd{get ;set;}
- //用戶姓名
- public string Name{get ;set ;}
- }
以上是最常見的用戶信息實體,你認為它職責單一嗎?說一說,我自己的看法:
站在用戶信息的角度來說,這個類代表的是用戶信息,它就是單一的,這也是大多數人的看法,有錯嗎?其實沒錯。因為在當前場景下,它確實是這樣。
隨著業務的發展,用戶的信息字段會越來越多,比如:用戶的年齡,性別,學歷....等等。看著越來越大的UserInfo類,是否該拆分呢?
這個時候我覺得你可以根據用戶信息的類型來進行拆分,畢竟大而全的類其實并不好。怎么拆分呢?比如:可以根據用戶登錄場景拆分出用戶認證的類型
- class UserAuth
- {
- //用戶id
- public int UserId{get ;set ;}
- //用戶登錄賬號
- public string Account{get;set ;}
- //用戶登錄密碼
- public string Pwd{get ;set;}
- }
可以根據用戶信息在系統中的出現頻率和重要度拆分出用戶基本信息和用戶擴展信息
- class UserBasicInfo
- {
- //用戶id
- public int UserId{get ;set ;}
- //用戶姓名
- public string Name{get ;set ;}
- //用戶手機號
- public string Phone{get ;set ;}
- //其他基本屬性
- }
- class UserExtendnfo
- {
- //用戶郵箱
- public string Email{get ;set ;}
- //用戶QQ號
- public string QQ{get ;set ;}
- //其他屬性
- }
當然這里我只是舉個栗子,如果用戶的Email和手機號一樣常用,可以把Email屬性提到基本屬性中。
以上只是以用戶信息為例,根據不同的用途進行拆分的一個栗子。在不同的業務背景下,不同的業務階段,對同一個類的拆分可能會有很大不同。有的時候,你所認為的"正確“會隨著系統的發展慢慢變成”錯誤“,當然這種”錯誤“并不可怕,畢竟系統的架構都是慢慢迭代出來的。
總之呢,評價一個類是否一定滿足單一原則,并沒有一個統一標準和規范,在實際的開發中,也沒有必要進行過度設計,在項目初級,完全可以是一個滿足業務需求的大而全的類,隨著業務的發展,你必然會經歷拆分的過程,這也是軟件發展的必然階段。
以上只是針對類這個最基本的面向對象單位來聊了聊,上升到模塊以及系統也是一樣的道理,微服務也是隨著軟件開發的不斷演進而出現的,其實從職責上來看,微服務也是職責單一原則的產物,而這個這則單一更多的是傾向于業務單一性,并非功能單一性。
那職責拆分的越細越好嗎?我不這么認為,當一個類或者模塊甚至系統,被拆分過細的時候,就會面臨著維護的問題,拿微服務來說,當微服務的數量過多,就會面臨著治理等一系列問題,這也是K8s要解決的問題之一。
拆分原則
說到底,雖然職責單一很難在主觀上給予準確判斷,但是還是有一些通用規則可以借鑒,這里以類為例
- 高內聚。系統在修改任一功能的時候,只需要修改一處地方,如果你需要修改多處才能滿足某個需求,很有可能你的職責劃分的不合理
- 屬性過多。當一個類屬性過多的時候,可以考慮把這個類進行職責的拆分。而至于多少個才算多呢?當查找某個屬性令你頭疼的時候,說明已經到了可以拆分的程度了(自己杜撰)
- 依賴過多。當一個類型中依賴的資源過多的時候,可以進行拆分
- 獨立變化。當一個類的某些屬性被大量使用而且會經常變化的時候,可以考慮把這些屬性進行拆分成獨立的類。
說到職責單一,這里順便提一下接口的設計,接口的設計更要遵循職責單一的原則,接口本質上是對業務的抽象,不同的業務應該抽象成不同的接口,以保證每個類,每個模塊,每個系統都可以獨立擴展。
寫在最后
沒有絕對好的方法可以讓所有人都認為你的拆分是正確的“職責單一”,有的時候,怎么樣才能職責單一真是要靠“靈感”
本文轉載自微信公眾號「架構師修行之路」,可以通過以下二維碼關注。轉載本文請聯系架構師修行之路公眾號。