2018年12月30日

單一職責原則。到底什麼是改變的理由?

Single responsibility principle(SRP). What defines a reason to change?

在物件導向開發中大家或多或少聽過SOLID,SOLID的每個字母分別代表了物件導向開發中的五個原則,本篇的重點是其中的S,也就是Single responsibility principle(SRP),單一職責原則。

什麼是單一職責原則呢?最廣泛也最常聽到的的定義是Robert C. Martin所說的:

A class should have only one reason to change.

翻譯成:一個類別只能有一個改變的理由(原因)。

那麼問題來了,到底什麼改變的理由(原因)?

類別的責任

首先我們要先釐清,什麼是類別的責任

當人們有需求的時候,我們會撰寫程式,設計類別,來滿足他們的需求。每個類別的都是因為需求而生的,用來提供服務,滿足使用對象的需求。

那所謂的對象呢,可以是實體的使用者,也就是人,也可以是抽象的程式,像是其他類別。

因為類別負責對這些對象提供服務,因此自然地,類別的責任就是滿足這些對象的需求,換句話說,類別對這些對象負責。

舉例來說,電商平台上的顧客,希望平台提可以供購物車功能,開發人員為此設計出了一個購物車類別。

透過這個類別,使用者可以將商品加入購物車,那就可以說,這個購物車類別對平台上的顧客提供了服務。

單一職責

既然類別的責任就是滿足對象的需求,那麼單一職責原則所說的就是,一個類別只能、只需滿足單一對象的需求,只能對一種對象提供服務。

購物車只需要滿足顧客的需求,不需要滿足公司內部會計部門的需求、不需要滿足資料分析單位的需求⋯⋯,我們可以另外設計其他類別來負責跟這些其他的對象溝通,提供這些對象服務,但是不會把這些需求通通放在購物車類別中。

購物車類別中,也不用管商品怎麼被結帳的,結帳這個責任,是別的類別的事情。

一個類別,一次只服務一個對象,他唯一的責任就是滿足這個對象的需求,這就是單一職責。

單一職責原則

再次回到這句話

A class should have only one reason to change.

當我們了解到單一職責代表的是:只對單一對象負責,那麼這個單一對象就是類別的唯一的需求變更來源,這個對象的需求變更,是這個類別唯一的改變的理由。

類別變化的理由來自類別所服務的對象。因為只有單一服務對象,所以只有一個理由來源。

白話一點就是:「就是那個XXX害我要修改這個類別啦」,這個XXX就是那個理由,都是因為XXX的關係,才要修改類別。

因為一個類別只有單一職責,單一服務對象,所以不會有另外其他的對象,要求我們修改類別。

例如說,購物車只需要滿足唯一對象:顧客。

除非顧客想要改變購物車一次可以加入的商品數目,否則我們不用需要去修改購物車。因為不需要顧慮資料分析部門的需求,資料分析部門要修改統計方式的時候,不需要修改購物車類別。

單一職責的好處

當一個類別只負責回應一個對象的需求時,不會為了應付其他人的需求而修改程式,可以專心服務負責對象,因次可以減少這個類別被修改的次數,藉而減少因為修改而產生bug的機率。

同時,因為一個類別只對一個對象負責,這個類別擁有了高內聚力(cohesion),不會包含一大堆不相干程式碼,讓這個類別的責任很明確。

此外當代物件導向程式開發,通常需要搭配自動化測試,當我們的類別只關注自己的責任,其他事情都透過依賴注入的方式或者是交由其他類別處理的話,要進行自動化測試就會比較容易。

後話

我認為單一職責原則最難的部分是定義對象,要定義清楚這個類別究竟是為了服務什麼對象,以及甚麼需求是相當困難的。即便是類似的功能,在不同組織、不同商業模式,也會有不同的運作方式,因此不會有標準答案,必須要仰賴經驗,以及不斷的嘗試。但是也不必為了追求完美而止步不前,總是有一些模糊地帶。

就我個人的經驗來說,同樣都是購物車類別,如果是在同一間公司,或是類似商業模式的公司裡面的工程師,設計出來的購物車類別,雖然會有差異,但其實核心的功能都差不多。

回到改變的理由,雖然本文中試著說明了,我覺得還是有些難理解,其實後來Robert C. Martin也知道reason to change帶來了不少困惑,因此他後來補充了SPR原則:

This principle is about people.

只有人(對象),才會要求改變。人,就是那個理由。

如果覺得這樣仍然太抽象,我個人是習慣的方式是,當我自己沒辦法用一小段話去敘述一個類別負責做什麼的時候,它就負責太多職責了,需要被職責分離。

參考資料

站內相關文章連結