嘻哈說:設計模式之單一職責原則

一、定義

首先呢,咱們來看一下單一職責原則的定義。html

就一個類而言,應該只有一個引發它變化的緣由bash

這個說法不是很好懂,有一些抽象,不過呢,咱們依舊能夠嘗試着理解一下。app

就一個類而言,只有一個引發它變化的緣由,也就是說,除此以外,不能有其它引發變化的緣由。學習

這樣就須要一個前提,這個類只能負責一項職責,而不能負責其餘的職責,否則,其餘的職責就會存在其餘變化的緣由了。優化

通俗的說,即一個類只負責一項職責ui

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

懶人曾經總結過:通俗的定義,淺顯易懂;理論的定義,大腦一懵code

有同感的小夥伴請雙擊666。htm

二、場景

餐館聚餐,經過服務員點餐對象

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

這樣一個服務員爲咱們點餐的場景,通常都是什麼樣的流程?

第一步:客人點餐

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

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

服務員:你是顧客,你是上帝,你說啥就是啥,不過,你剛纔說的是啥。。。

第二步:烹飪美食

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

第三步:上餐

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

三、實現

不廢話,擼代碼。

package com.fanqiekt.principle.single;

/**
* 服務員
* @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 + "上桌啦,請您品嚐");
    }
}
複製代碼

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

爲了更好的理解,懶人進行了細節的優化(主要是不少細節懶人壓根不瞭解)。

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("酸辣土豆絲");
    }
}
複製代碼

客戶端這個類就至關於客人,客人負責經過服務員點餐。

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

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

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

咱們回過頭來看一下waiter類,你們以爲這個類好很差?

確定是很差了,那...很差在哪裏?

這就比如一個小做坊,老闆既負責點餐又負責下單,就跟waiter類同樣。

咱們通常在小做坊吃飯,感覺會怎麼樣?

亂,非同通常的雜亂。上菜須要等半天,點餐的時候找不到人。

還有一個弊端,我修改了作飯的流程,會影響下單的業務,增長修改的風險,爲何這麼說呢?

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

老闆:好嘞,您稍等。

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

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

客人A:老闆,個人菜作了嗎?個人同伴都吃完了,沒作我就不要了!

老闆:您的菜已經作了,立刻就要出鍋了。(心裏:我勒個去,剛纔用小勺放鹽的時候把這哥們點的單給忘了,這就尷尬了。。。)

不難看出,當功能冗雜到一個對象中,這樣修改就會增長風險。那咱們該如何避免呢?

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

廚師作飯,服務員點餐,這樣作,有什麼好處呢?

一來,結構清晰了,各司其職,一目瞭然。二來,風險下降了,我修改作飯的流程,不會影響下單的業務。

只負責一項職責,這就是單一職責原則。

那咱們嘗試着增長一個廚師類。

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 + "出鍋");
    }
}
複製代碼

廚師類,只負責了一項職責:作飯。

這就是類的單一職責原則。

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

不合適的,cooking方法應該只有菜品分發這一項職責,而炒西紅柿雞蛋以及酸辣土豆絲這兩件事顯然易見與分發沒有任何關係,因此拆分出來效果會更好。

咱們將廚師類再優化下。

package com.fanqiekt.principle.single;

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

    /**
     * 作飯
     * 方法的單一職責原則
     * @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("...");
    }
}
複製代碼

優化後Chef類有三個方法。

cooking方法是根據下單的菜品名稱去烹飪不一樣的菜。

cookingTomato方法是炒西紅柿雞蛋。

cookingPotato方法是炒酸辣土豆絲。

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

遵照方法單一職責原則的類,是否是更加的直觀?修改各自的方法是否是也沒有影響到其餘的方法?

接下來,咱們再優化下Waiter類,讓他遵循類的單一職責原則。

package com.fanqiekt.principle.single;

/**
 * 單一職責原則的服務員
 *
 * @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+"上桌啦,請您品嚐!");
    }
}
複製代碼

優化後SingleWaiter類有隻負責點餐、上餐這些與服務員相關的職責,而作飯的這些無關的職責則交給了Chef。

遵照類單一職責原則的項目,是否是更加的直觀?修改各自的類是否是也沒有影響到其餘的類?

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

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

結果與原來一致。

四、優勢

擼過代碼後,咱們發現單一職責原則的幾個優勢。

提升類的可讀性

符合單一職責原則的方法、類,結構會更加的清晰,類的可讀性也就提升了。

下降類的複雜性 一個類只負責一項職責,一個方法也只負責一項職責。確定要比功能冗雜到一個方法,一個類中要簡單得多。

下降風險

修改其中的一個業務,不會影響到業務。

五、總結

咱們必需要意識到,一味的遵照單一職責原則,不停的分拆類所付出的開銷是很大的。

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

懶人的觀點是若是一個方法邏輯不復雜的狀況下,能夠修改方法實現,不然要拆分爲兩個方法,遵循方法級別的單一職責原則。

若是一個類方法很少的狀況下,能夠只增長方法,而不用分拆爲多個類,不然要拆分爲多個類,遵循類級別的單一職責原則。

六、嘻哈說

接下來,請您欣賞單一職責原則的原創歌曲

嘻哈說:單一職責原則
做曲:懶人
做詞:懶人
Rapper:懶人

週末約上了好友去熟悉的餐館聚餐
只負責點餐的漂亮服務員保持笑容已經成爲習慣
只負責作飯的帥氣廚師一直待在了煙霧瀰漫了幾遍的廚房裏面
每一個人有本身負責的地盤
就像單一職責
一個類只有一個職責 好體面
它下降了類的複雜性
它提升了類的可讀性
那風險被下降表明着單一職責沒毛病
複製代碼

試聽請點擊這裏

閒來無事聽聽曲,知識已填腦中去;

學習複習新方式,頭戴耳機不小覷。

番茄課堂,學習也要酷。

相關文章
相關標籤/搜索