單一職責原則(single responsibility principle, srp)是bob大叔提倡的s.o.l.i.d五大設計原則中的第乙個。其中,職責(responsibility)被表述為「變化的原因」(reason to change);srp被表述為「乙個類應該有且只有乙個變化的原因」。但如果光從字面去理解,srp很容易讓人望文生義產生誤解。本文希望能闡明srp的本質,達到避免誤解和指導設計的目的。
動機
對於設計原則的理解應該首先從它的動機入手,即遵循這個原則帶來的好處是什麼?對於srp來講,如果遵循「乙個類應該有且只有乙個變化的原因」是因,那麼「任何乙個變化只會影響乙個類」就是果。可見srp的動機主要是系統的可維護性,即降低適應變化的成本。
多功能與單變化
對於單功能的類來講,比較容易遵循srp,比如:把string轉換為datetime型別的工具類。理解srp的難點在於在多功能類的情形,即不容易理解多功能與單變化的矛盾。讓我們先來看乙個modem類的例子,modem具有4個功能:撥號,結束通話,傳送資料,接收資料:
class modem
public void hangup()
public void send(char adata)
public char receive() }
我們先來考察一下modem類的變化點,並將其分為兩類:1.modem類的內部細節變化,比如dial、hangup、send、receive等具體實現發生了變化;2.modem類整體性的變化,比如modem類需要增加poweron和poweroff方法。上面的modem類具有多個變化點,不符合srp。
抽象與抽象層次
但是,需要注意的是上面的modem類其實是很符合現實的概念模型的,modem本身就應該具有這幾個功能點,也就是說dial、hangup、send、receive方法在modem這個類中是高內聚關係。難道要把modem類強行拆開嗎,難道所有類都只應該有乙個方法嗎?是什麼原因導致符合現實概念模型的modem類設計與srp不符合呢?問題在於抽象與抽象層次,modem類是現實概念模型的抽象沒有錯,但是modem類缺乏抽象層次,低層概念與高層概念混雜,這正是問題所在。下面是重構以後的modem類:
inte***ce idialable
class dialer : idialable }
class modem
private idialable m_dialer;
… //hangup, send, receive類似 }
上面的設計將各個功能點抽象為單一的介面,將變化封裝在穩定的介面背後,再通過組合的方式建立起整體的modem類與區域性的功能點的聯絡。這樣就避免了低層的變化傳導到高層。
總結
類只有乙個變化的原因 >> 乙個變化只影響乙個類 >> 變化只影響其相應層次的類
參考
《the single responsibility principle》
《冒號課堂》
《瑞士軍刀vs單一職責原則》
單一職責原則
定義 不要存在多於乙個導致類變更的原因。通俗的說,即乙個類只負責一項職責。問題由來 類t負責兩個不同的職責 職責p1,職責p2。當由於職責p1需求發生改變而需要修改類t時,有可能會導致原本執行正常的職責p2功能發生故障。解決方案 遵循單一職責原則。分別建立兩個類t1 t2,使t1完成職責p1功能,t...
單一職責原則
單一職責原則 乙個類,只有乙個引起它變化的原因。應該只有乙個職責。每乙個職責都是變化的乙個軸線,如果乙個類有乙個以上的職責,這些職責就耦合在了一起。這會導致脆弱的設計。當乙個職責發生變化時,可能會影響其它的職責。另外,多個職責耦合在一起,會影響復用性。例如 要實現邏輯和介面的分離。對於user類,裡...
單一職責原則
問題由來 一心二用,效率降低 類t負責兩個不同的職責 職責p1,職責p2。當由於職責p1需求發生改變而需要修改類t時,有可能會導致原本執行正常的職責p2功能發生故障。解決方案 專注做某件事情 遵循單一職責原則。分別建立兩個類t1 t2,使t1完成職責p1功能,t2完成職責p2功能。這樣,當修改類t1...