“分離職責”是經常使用的一個重構政策,當一個類擔任的職責太多時,應按職責将它拆分成多個類,每個類分别承擔“單一”的職責,也就是讓每個類專心地做“一件事情”。就一個類而言,應該僅有一個引起它變化的原因,這是所謂的SRP原則。本篇文章基于SRP原則展開描述了SRP原則的定義,為什麼要遵循SRP原則,以及實踐SRP原則的難點。最後,我們通過一則示例來說明“分離職責”這個重構政策。
“分離職責”是經常使用的一個重構政策,當一個類擔任的職責太多時,應按職責将它拆分成多個類,每個類分别承擔“單一”的職責,也就是讓每個類專心地做“一件事情”。
在面向對象程式設計中,SRP原則是一個非常重要的原則(SOLID原則都很重要),在展示示例前,我們先了解一下SRP原則是什麼,以及它有什麼作用。
SRP原則的定義是這樣的:
There should never be more than one reason for a class to change.
就一個類而言,應該僅有一個引起它變化的原因。
When a class has more than one responsibility, there are also more triggers and reasons to change that class. A responsibility is the same as “a reason for change” in this context.
因為每一個職責都是變化的因子,當需求變化時,該變化通常反映為類的職責的變化。
如果一個類承擔了多于一個的職責,那麼就意味着引起它的變化的原因會有多個,等同于把這些職責耦合在了一起。
一個職責的變化可能會抑制到該類完成其他職責的能力,這樣的耦合會導緻脆弱的設計。
在設計類或接口時,如果能夠遵守SRP原則,則會帶來以下優點:
類的複雜性降低:每個類或接口都隻定義單一的職責,定義清晰明确
可讀性提高:定義清晰明确,自然帶來較高的代碼可讀性
可維護性提高:代碼可讀性提高,意味着更容易了解;單一職責使得類之間的耦合性較低,更改也會較為容易
擴充性更好:當需要擴充新的職責時,隻需要定義新的接口和新的實作即可
Separating responsibility can be done by defining for every responsibility a class or an interface.
應該為每一項職責定義類或接口的方式實作職責分離。
SRP原則堪稱是SOLID原則裡面最簡單的一個原則,但也可以說是最難的一個。
它的“簡單”之處在于它很容易被了解,“困難”之處在于很多人在軟體設計過程中,很難真正地抓住關鍵點。
對于開發者來說,“分離職責”存在四個難點,也是開發者在使用這種重構政策時需要慎重考慮的地方
“職責”單一是相對的,每個人看事情的角度,對業務的了解程度是不盡相同的,這導緻了人們對職責的定義和細化程度的差異性。同樣一個業務,有些人從角度A出發,在對業務提煉歸納總結後,得出三項職責:J、K、L。而有些人則從角度B出發,歸納總結出兩項職責:X、Y。
在設計接口時,這兩人自然而然地會設計出不同的接口,兩人設計的接口個數和表達的語義也各不相同。
拿裝修房子來說,業主通常會委托裝修公司來做這件事兒,站在業主的角度了解,它就是一件大事兒——“裝修公司裝修房子”,至于怎麼裝修,由裝修公司來搞定。
裝修公司通常會将這件大事兒拆分成幾件小事兒,譬如:“室内設計”、“貼瓷磚”、“做家具”、“刷牆”等等,然後再去雇傭不同類型的勞工來完成這些小事兒。
職責和類的命名應該比對,如果在職責歸納時,歸納出的職責比較模糊,可能會使類的命名變得艱難。
另外,即使你歸納出的職責是清晰的,如果命名與職責不符(詞不達意),仍然會給将來的維護、再重構帶來一些困難(命名是非常非常重要的)。
将多個職責被拆分到多個類時,原本在一個類中展現的職責被分散到多個類了,與此同時也需要考慮類的粒度。
粒度應當适中,粒度的控制沒有固定的标準,這需要結合業務場景具體分析。
原本用一個類就能完成的功能,現在需要結合多個類才能完成。
現在為了確定原有的功能仍然能正常運作,較大可能會形成多個類之間的依賴關系。
如果有些類被遷移到其他工程了,這還會涉及到工程之間的依賴關系。
“分離職責”是比較難的一個重構政策,尤其是在一些大型項目中。
該政策如果不能良好地利用,可能會讓你的工程或解決方案變得不倫不類。
如果你的重構經驗較淺,建議你從一些較小的項目練習這項重構政策。
這段代碼包含兩個類Video和Customer。
Viedo類包含三個職責:支付費用、租借Video和計算租金。
Video的職責太多,它把Customer類的職責也“搶”過來了。
在歸納職責時,我們可以通過識别主語的方式來确定其歸屬。
支付費用的主語是“客戶”,即“客戶支付費用”。
計算租金的主語也是“客戶”,即“計算客戶的租金”。
是以,我們可以将Video類的PayFee()、CalculateBalance()方法放到Customer類中。
原則就像基準線,在設計類和接口時,我們應該盡量遵守基準線,而不是死守基準線,在設計時不應死闆地依照原則進行設計。這就好比開車,司機的視線應該始終保持在正前方,如果沿着公路上的線開車而忽視了前方的交通情況,可能會引發交通事故。
小酌重構系列目錄彙總
關注keepfool
本文連結:
文章作者:keepfool
文章出處:http://www.cnblogs.com/keepfool/
如果您覺得閱讀本文對您有幫助,請點一右下角的“推薦”按鈕,您的“推薦”将是我最大的寫作動力!歡迎看官們轉載,轉載之後請給出作者和原文連接配接。