單一職責原則 ( SRP Single Responsibility Principle ) 之前在談論這個主題時,有提到《 Clean Architecture 》在這的定義如下 :
一個元件只有一個
角色
引起變化
然後當時我自已的想法為 :
角色代表這個類別的使用者(即實際執行方法的地方),而變化則是指因為這個角色的某個需求,導致需要修改程式碼。
後來,我參考了《Clean Architecture 實作篇(第二版)》中的定義:
每個元件都應該僅有一種且唯一的被修改的理由
他有給一個範書中提供了一個範例,對我來說是很容易理解的::
A → B + C + D + E ( A 依賴 B + C + D + E,就是 A 有用這些元件的啥 )
B → E
C → E
上面的範例代表這 :
因此,A 的修改理由有 B + C + D + E,所以它有 4 種修改的理由,而 E 的修改理由僅有它自己需要加新功能,只有 1 種。因此,我認為 SRP 的被修改的理由可以總結如下圖:
然後以程式碼來看如下 :
使用者情境
。依賴的元件
。function getOrdersByUserId (user: User){
if(user.role !== 'USER'){
throw new Error('the user is not USER role')
}
const orders = await orderRepository.getOrders({ userId: user._id})
return orders;
}
DIP Dependency inversion principle (DIP) 是後面會提到的元則,它的主要重點在於 :
高層次的模組不應該依賴於低層次的模組,兩者都應該依賴於抽象介面。抽象不要依賴細節,細節要依賴與抽象
這個原則的實際應該用有一部份就是用來處理依賴元件變動的部份,所以 SRP 與 DIP 實際上是很有關係的。
然後這幾年後又有不少新的感受,想來談談。
首先在《 Clean Architecture 》所提到的角色這個概念我後來發現是有一點模糊的,比較準確的說是每個人的定義會不同,例如有人覺得是用這個 class 的地方 ? 或是實際上用這個 class 的實際角色 ( 例如 api 權限設計 ) 或者是 class 本身所代表的角色。。
然後接下來 《 Clean Architecture 實作篇 》提到的,我自已覺得是很好理解,但如果要做到這裡說的,那是不是要像 E 一樣,或是說每個元件只能依賴一個元件而以 ? 好像那裡怪怪的對吧 ?
後來我自已在總結以下,然後變成我自已在開發時,常常會想思考的 3 件事 :
例如我有一個方法叫 getOrdersByUserId,那它是否符合 SRP 呢 ?
function getOrdersByUserId (user: User){
if(user.role !== 'USER'){
throw new Error('the user is not USER role')
}
const orders = await orderRepository.getOrders({ userId: user._id})
return orders;
}
先想一下他現在的使用情境。他現在是用來處理『 讓用戶前台看到他的所有訂單 』的需求,然後下面是我腦袋開發時會想的事情 :
所以嚴格來說這個方法是沒有符合 SRP 的,比較好的修改方向我自已覺得是以下兩個 :
然後基本上走到後面,應該就慢慢的變成 1 + 2 一起使用了。
然後這裡也想順到提一下 :
Naming 影響到
範圍
,也就是說你取名越抽象,例如 getUsers 這也代表假設了任何人都可以叫這隻,但裡面實作有沒有符合,就又是另一個問題了。
被修改的理由
會增加。情境與角色
會增加。不過認真的說,我也很難說不要太多的依賴是多少依賴,不要太抽象是多抽象才算,很多時後只能靠感覺…,但通常你發現一個 class 好大,通常十之八九就是有問題了,所以我通常在 code review 時,只會抓個大概,大部份都是回來重構時,再來看看。