程序設計原則-單一職責原則

含義:就一個類而言,應該僅有一個引起它變化的原因。
字面解釋:對于一個類而言,所應當擁有的職責應該只有一個。
KISS(keep it stupid and simple)原則要求盡量簡單,假如一個類負責了多個任務,則是違背了這個原則。
單一職責原則從理解上講非常容易懂,但是實際應用中,會遇到很多阻礙。比如,我們單個類的職責簡單了,但對于實現(xiàn)一個具體功能,往往會需要做很多事情,如果一個類只能負責一個職責的話,那么可能會需要非常多的類。同時,職責的拆分也會帶來分析的復雜度。

日常生活的例子

比如,日常做飯的例子,我們需要一個廚師。如果簡單就這樣一個需求的話,那么僅僅一個廚師就夠了。但是,在復雜一點的場景下,廚師要做很多菜,他就沒有時間去買菜,他需要有人,給他準備材料。這個時候做飯這件事情,又衍生出了準備材料這個職責。而這個職責原本是廚師就可以干的事情。有做飯的材料可能還不夠,為了能夠加快效率,廚師可能還需要人將這些材料加工好,比如,洗菜,切菜等等。
正如上面所描述的那樣,一個做飯的需求,就包含了買材料,加工材料,烹飪這幾個工作。如果幾個工作都比較簡單,則可以直接劃分到廚師的工作范圍內(nèi),我們給封裝一個方法叫做cook()就完成了,而里面的幾個工作,其實就是拆分的小步驟而已。
而當其中的任何一個步驟都變得比較復雜的情況下,那么,我們的拆分就變得有必要了。
比如,買材料要涉及到去超市,選擇食材,結賬等等事情,加工材料包括清洗,切片等等。
而烹飪操作則又包括了(以做酸菜魚為例)

  1. 將魚肉切片放入盆中, 加入蛋清、料酒、淀粉、鹽,攪拌均勻。 腌制20min。
  2. 將鍋燒熱之后倒入少許油,蓋過鍋底。放入姜片、蒜泥、郫縣豆瓣醬,炒至可以聞到香味。
  3. 放入切碎的酸菜絲翻炒均勻,倒入足量的開水,水量需要沒過鍋內(nèi)的材料以及即將放入的魚片等,待水沸騰之后放入金針菇段。
  4. 最后將魚片一片片加入鍋中并且用筷子撥散,一直煮到所有魚片變色之后即可出鍋。

原本只需要一個廚師,后面開始需要買菜員買菜,加工人員洗菜切菜。
以上就是我們現(xiàn)實生活中對于職責這個事情由簡單到復雜的演變過程。
往往在業(yè)務之初,我們常常能夠滿足單一職責的需求。但是在開發(fā)過程中由于業(yè)務變得復雜,以至于原有的類不再符合單一職責原則。這就是程序變得復雜、代碼出現(xiàn)壞味道的緣由。
我們要做的工作便是,在合適的時候將類進行拆分,具體在什么時候拆分,則需要大家在業(yè)務中進行平衡。

代碼開發(fā)的例子

需求的變動

我們需要開發(fā)一個接口,功能是接收用戶前臺的一個修改用戶信息的請求。
對于這個需求,可能只需要一件事情,就是更改數(shù)據(jù)庫
那么我們一般情況下,會通過一個接口類接入請求,封裝DTO交給業(yè)務類BusinessService,在業(yè)務類中調用DAO將數(shù)據(jù)更新到數(shù)據(jù)庫中
我們圍繞業(yè)務類展開:
第一次需求變動,我們需要對入?yún)⑦M行校驗,比如用戶id必填校驗
此時,變動比較簡單,我們直接在BusinessService中加個if-else邏輯判斷,不符合條件的,直接返回即可。
第二次需求變動,我們需要對用戶的手機號碼進行校驗,手機號碼的格式校驗。
此時,變動還是比較簡單,我們直接在BusinessService中加個if-else邏輯判斷,不符合條件的,直接返回即可。
第n次需求變動,我們需要對用戶的地址進行校驗,用戶地址不能為空校驗。
此時,已經(jīng)有了n個if-else的代碼塊,這個時候,我們單純通過數(shù)if-else的個數(shù)就已經(jīng)可以確認,該類已經(jīng)開始變得復雜了。但是主體業(yè)務邏輯其實沒有變。

“就一個類而言,應該僅有一個引起它變化的原因”

簡單的拆分

這個原則,此時其實已經(jīng)被打破。我們迫切地需要將校驗的邏輯拆分出去。
這個時候,我們可以通過建一個校驗類Validator的方式,將所有對于參數(shù)校驗的邏輯都放到該類中的校驗方法validate()中,此時,對于BusinessService類而言,他其實就是做了兩件事,第一件事通過Validator 校驗參數(shù),通過DAO將數(shù)據(jù)更新到數(shù)據(jù)庫中。
這種是最簡單的業(yè)務拆分方案。

復雜的業(yè)務

后面,需求很可能會還有變化。
用戶級別限制,對改id的需求,還需要看是否已經(jīng)達到當年修改id的上限。不同級別的用戶修改id的上限是不同的。
改名卡規(guī)則:修改id的時候,還要看是否有改名卡,如果有的話,則要消耗一張改名卡,
計費邏輯:需要消耗虛擬金幣的情況下,還要調用外圍的計費系統(tǒng)進行虛擬扣費。。。巴拉巴拉,一大堆的東西。
在業(yè)務上都是有可能的。因此簡單的一個業(yè)務需求,按照傳統(tǒng)寫法,來一個需求,就加一段邏輯,最后該類中,會出現(xiàn)奇奇怪怪的代碼片段,維護的時候,會變得異常復雜。

適時合理地拆分

我們要做的是,在合適的時機將業(yè)務剝離出來。
判斷會員級別并確定是否可以修改的邏輯,需要交給會員類Member,通過調用Member類來判斷是否可改。
扣費的邏輯則交給計費類Charging。
那么以后如果是在參數(shù)校驗上的邏輯改變、會員級別調整邏輯上的改變、數(shù)據(jù)庫層字段上面的改變,都不會影響到BusinessService類,僅有一個引起它變化的原只有,大的業(yè)務邏輯上又要加上或者去掉某個功能。
代碼主干的邏輯應該是

class BusinessService{
    public void doBusiness(){
        Validator.validate();
        Member.doMemberBusiness();
        Charging.charge();
        SomeOther.otherBusiness();
        DAO.save();
    }
}

持續(xù)治理,持續(xù)重構

實際開發(fā)中可能情形更加復雜,我們需要就事論事進行處理,同時要深深的牢記這個原則,記住,代碼是慢慢產(chǎn)生壞味道的。持續(xù)治理,持續(xù)重構,則是良藥

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容