嘻哈說:設(shè)計模式之單一職責(zé)原則

image

1、定義

首先呢,我們來看一下單一職責(zé)原則的定義。

就一個類而言,應(yīng)該只有一個引起它變化的原因

這個說法不是很好懂,有一些抽象,不過呢,我們依舊可以嘗試著理解一下。

就一個類而言,只有一個引起它變化的原因,也就是說,除此之外,不能有其它引起變化的原因。

這樣就需要一個前提,這個類只能負責(zé)一項職責(zé),而不能負責(zé)其他的職責(zé),不然,其他的職責(zé)就會存在其他變化的原因了。

通俗的說,即一個類只負責(zé)一項職責(zé)。

懶人就比較喜歡這種通俗地定義,一目了然。

懶人曾經(jīng)總結(jié)過:通俗的定義,淺顯易懂;理論的定義,大腦一懵

有同感的小伙伴請雙擊666。

2、場景

餐館聚餐,通過服務(wù)員點餐

這是一個比較常見的場景,比如懶人擼了五天的代碼,身心疲憊,周末的時候呢,就約上三五個好友,去餐館(番茄餐廳)happy一下(非常單純的吃飯)。我們剛剛坐下,就來了一位很漂亮的服務(wù)員為我們點餐。

這樣一個服務(wù)員為我們點餐的場景,一般都是什么樣的流程?

第一步:客人點餐

懶人:咱呢,不但要吃飽,還要吃好!服務(wù)員,先來一份西紅柿炒雞蛋,再來一份酸辣土豆絲?。?!

好友:臉呢。。。說好的臉呢。。。

服務(wù)員:你是顧客,你是上帝,你說啥就是啥,不過,你剛才說的是啥。。。

第二步:烹飪美食

西紅柿炒雞蛋,先炒雞蛋,再炒西紅柿。。。ok,出鍋。

第三步:上餐

服務(wù)員:這是您點的西紅柿炒雞蛋,請您慢用。

3、實現(xiàn)

不廢話,擼代碼。

package com.fanqiekt.principle.single;

/**
* 服務(wù)員
* @Author: 番茄課堂-懶人
*/
public class Waiter {

    /**
    * 下單
    * @param dishName 菜名
    */
    public void order(String dishName){
        System.out.println("客人點餐:" + dishName);

        System.out.println("開始烹飪:" + dishName);
        //菜品不同,做法不同。
        switch (dishName){
            case "西紅柿炒雞蛋":
                System.out.println("先炒雞蛋");
                System.out.println("再炒西紅柿");
                System.out.println("...");
                break;
            case "酸辣土豆絲":
                System.out.println("先放蔥姜蒜");
                System.out.println("再放土豆絲");
                System.out.println("...");
            break;
        }
        System.out.println(dishName + "出鍋");

        System.out.println(dishName + "上桌啦,請您品嘗");
    }
}

服務(wù)員這個類比較簡單,就一個下單的方法。

為了更好的理解,懶人進行了細節(jié)的優(yōu)化(主要是很多細節(jié)懶人壓根不了解)。

package com.fanqiekt.principle.single;

/**
 * 客戶端
 * @Author: 番茄課堂-懶人
 */
public class Client {
    public static void main(String[] args){
        Waiter waiter = new Waiter();
        waiter.order("西紅柿炒雞蛋");
        System.out.println("-------");
        waiter.order("酸辣土豆絲");
    }
}

客戶端這個類就相當(dāng)于客人,客人負責(zé)通過服務(wù)員點餐。

客人一共點了兩道大餐,西紅柿炒雞蛋、酸辣土豆絲,我們來運行一下,看看結(jié)果。

客人點餐:西紅柿炒雞蛋
開始烹飪:西紅柿炒雞蛋
先炒雞蛋
再炒西紅柿
...
西紅柿炒雞蛋出鍋
西紅柿炒雞蛋上桌啦,請您品嘗
-------
客人點餐:酸辣土豆絲
開始烹飪:酸辣土豆絲
先放蔥姜蒜
再放土豆絲
...
酸辣土豆絲出鍋
酸辣土豆絲上桌啦,請您品嘗

OK,兩個熱氣騰騰的飯菜就做好了。

我們回過頭來看一下waiter類,大家覺得這個類好不好?

肯定是不好了,那...不好在哪里?

這就好比一個小作坊,老板既負責(zé)點餐又負責(zé)下單,就跟waiter類一樣。

我們一般在小作坊吃飯,感受會怎么樣?

亂,非同一般的雜亂。上菜需要等半天,點餐的時候找不到人。

還有一個弊端,我修改了做飯的流程,會影響下單的業(yè)務(wù),增加修改的風(fēng)險,為什么這么說呢?

客人A:老板,給我來一份酸辣土豆絲。

老板:好嘞,您稍等。

懶人:老板,我剛才點的西紅柿雞蛋要少放鹽啊。

老板:好的,我放鹽的時候用小點的勺子。

客人A:老板,我的菜做了嗎?我的同伴都吃完了,沒做我就不要了!

老板:您的菜已經(jīng)做了,馬上就要出鍋了。(內(nèi)心:我勒個去,剛才用小勺放鹽的時候把這哥們點的單給忘了,這就尷尬了。。。)

不難看出,當(dāng)功能冗雜到一個對象中,這樣修改就會增加風(fēng)險。那我們該如何避免呢?

一般比較完善的餐館,還至少會有一名廚師。

廚師做飯,服務(wù)員點餐,這樣做,有什么好處呢?

一來,結(jié)構(gòu)清晰了,各司其職,一目了然。二來,風(fēng)險降低了,我修改做飯的流程,不會影響下單的業(yè)務(wù)。

只負責(zé)一項職責(zé),這就是單一職責(zé)原則。

那我們嘗試著增加一個廚師類。

package com.fanqiekt.principle.single;

/**
 * 廚師
 *
 * @author 番茄課堂-懶人
 */
public class Chef {

    /**
     * 做飯
     * @param dishName 下單的菜名
     */
    public void cooking(String dishName) {
        System.out.println("開始烹飪:"+dishName);
        switch (dishName){
            case "西紅柿炒雞蛋":
                System.out.println("先炒雞蛋");
                System.out.println("再炒西紅柿");
                System.out.println("...");
                break;
            case "酸辣土豆絲":
                System.out.println("先放蔥姜蒜");
                System.out.println("再放土豆絲");
                System.out.println("...");
                break;
        }
        System.out.println(dishName + "出鍋");
    }
}

廚師類,只負責(zé)了一項職責(zé):做飯。

這就是類的單一職責(zé)原則。

Chef類只有一個cooking方法,cooking方法是根據(jù)下單的菜品名稱去烹飪不同的菜,以及炒西紅柿雞蛋以及酸辣土豆絲的具體烹飪過程。這樣做合適嗎?

不合適的,cooking方法應(yīng)該只有菜品分發(fā)這一項職責(zé),而炒西紅柿雞蛋以及酸辣土豆絲這兩件事顯然易見與分發(fā)沒有任何關(guān)系,所以拆分出來效果會更好。

我們將廚師類再優(yōu)化下。

package com.fanqiekt.principle.single;

/**
 * 廚師
 *
 * @author 番茄課堂-懶人
 */
public class Chef {

    /**
     * 做飯
     * 方法的單一職責(zé)原則
     * @param dishName 下單的菜名
     */
    public void cooking(String dishName) {
        System.out.println("開始烹飪:"+dishName);

        switch (dishName){
            case "西紅柿炒雞蛋":
                cookingTomato();
                break;
            case "酸辣土豆絲":
                cookingPotato();
                break;
        }

        System.out.println(dishName + "出鍋");
    }

    /**
     * 炒西紅柿雞蛋
     */
    private void cookingTomato() {
        System.out.println("先炒雞蛋");
        System.out.println("再炒西紅柿");
        System.out.println("...");
    }

    /**
     * 炒酸辣土豆絲
     */
    private void cookingPotato() {
        System.out.println("先放蔥姜蒜");
        System.out.println("再放土豆絲");
        System.out.println("...");
    }
}

優(yōu)化后Chef類有三個方法。

cooking方法是根據(jù)下單的菜品名稱去烹飪不同的菜。

cookingTomato方法是炒西紅柿雞蛋。

cookingPotato方法是炒酸辣土豆絲。

每個方法只負責(zé)一項職責(zé),這就是方法的單一職責(zé)原則。

遵守方法單一職責(zé)原則的類,是不是更加的直觀?修改各自的方法是不是也沒有影響到其他的方法?

接下來,我們再優(yōu)化下Waiter類,讓他遵循類的單一職責(zé)原則。

package com.fanqiekt.principle.single;

/**
 * 單一職責(zé)原則的服務(wù)員
 *
 * @author 番茄課堂-懶人
 */
public class Waiter {
    private Chef chef = new Chef();

    /**
     * 點餐
     * @param dishName 餐名
     */
    public void order(String dishName) {
        System.out.println("客人點餐:"+dishName);

        chef.cooking(dishName);

        System.out.println(dishName+"上桌啦,請您品嘗!");
    }
}

優(yōu)化后SingleWaiter類有只負責(zé)點餐、上餐這些與服務(wù)員相關(guān)的職責(zé),而做飯的這些無關(guān)的職責(zé)則交給了Chef。

遵守類單一職責(zé)原則的項目,是不是更加的直觀?修改各自的類是不是也沒有影響到其他的類?

接下來,我們把Client運行一下。

客人點餐:西紅柿炒雞蛋
開始烹飪:西紅柿炒雞蛋
先炒雞蛋
再炒西紅柿
...
西紅柿炒雞蛋出鍋
西紅柿炒雞蛋上桌啦,請您品嘗
-------
客人點餐:酸辣土豆絲
開始烹飪:酸辣土豆絲
先放蔥姜蒜
再放土豆絲
...
酸辣土豆絲出鍋
酸辣土豆絲上桌啦,請您品嘗

結(jié)果與原來一致。

4、優(yōu)點

擼過代碼后,我們發(fā)現(xiàn)單一職責(zé)原則的幾個優(yōu)點。

提高類的可讀性

符合單一職責(zé)原則的方法、類,結(jié)構(gòu)會更加的清晰,類的可讀性也就提高了。

降低類的復(fù)雜性
一個類只負責(zé)一項職責(zé),一個方法也只負責(zé)一項職責(zé)??隙ㄒ裙δ苋唠s到一個方法,一個類中要簡單得多。

降低風(fēng)險

修改其中的一個業(yè)務(wù),不會影響到業(yè)務(wù)。

5、總結(jié)

我們必須要意識到,一味的遵守單一職責(zé)原則,不停的分拆類所付出的開銷是很大的。

這時候就涉及到平衡的問題,平衡單一職責(zé)原則與修改造成的開銷。

懶人的觀點是如果一個方法邏輯不復(fù)雜的情況下,可以修改方法實現(xiàn),否則要拆分為兩個方法,遵循方法級別的單一職責(zé)原則。

如果一個類方法不多的情況下,可以只增加方法,而不用分拆為多個類,否則要拆分為多個類,遵循類級別的單一職責(zé)原則。

6、嘻哈說

閑來無事聽聽曲,知識已填腦中去;學(xué)習(xí)復(fù)習(xí)新方式,頭戴耳機不小覷。
番茄課堂,學(xué)習(xí)也要酷。
接下來,請您欣賞懶人為單一職責(zé)原則創(chuàng)作的歌曲。

試聽請點擊這里

嘻哈說:單一職責(zé)原則

周末約上了好友去熟悉的餐館聚餐
只負責(zé)點餐的漂亮服務(wù)員保持笑容已經(jīng)成為習(xí)慣
只負責(zé)做飯的帥氣廚師一直待在了煙霧彌漫了幾遍的廚房里面
每個人有自己負責(zé)的地盤
就像單一職責(zé)
一個類只有一個職責(zé) 好體面
它降低了類的復(fù)雜性
它提高了類的可讀性
那風(fēng)險被降低代表著單一職責(zé)沒毛病
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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

  • 月 落在水里 魚 游在月里 風(fēng)來 浪起 月碎了 魚跑了 夢醒了
    童心_8c86閱讀 479評論 12 36
  • 作:流尋常 二月天給我們遇見 等候在十二月離別 雪天離別 卻不會說出沉默的再見 想起當(dāng)時畫面 就像是相隔了幾個世界...
    虛構(gòu)我的生活閱讀 322評論 0 2
  • 最近沒畫過了,手好生。本來就差,現(xiàn)在更差。 還是覺得自己超級廢柴 謝謝閱讀
    薄荷與自己和解閱讀 147評論 0 0

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