接口隔離原則

原文:
接口隔離原則(小夥子的作坊)
景

設計模式六大原則之四:接口隔離原則。

簡介

姓名 :接口隔離原則

英文名 :Interface Segregation Principle

價值觀 :寧缺毋濫

個人介紹

  1. Clients should not be forced to depend upon interfaces that they don't use.(客戶端不應該依賴它不需要的接口。)
  2. The dependency of one class to another one should depend on the smallest possible interface.(類間的依賴關係應該建立在最小的接口上。)

也用一個故事來講這 2 句乾巴巴的定義。

一小夥子跑到大城市的工廠打工,工作了一年半載,越來越覺得沒勁,每天干那麼多活,又領那麼一點工資,和他老爸抱怨這段時間的困擾,老爸想着,家裏有個小作坊,自己也一年不如一年了,要不就讓兒子回老家管理這小作坊。小夥子熬不過這個年,就跑回老家跟着老爸打理小作坊。

布娃娃
(來自Google Image)

小作坊主要是做布娃娃的,如上圖,工作在於打扮包裝布娃娃,工序有給布娃娃扎辮子、穿衣服、包裝入箱、打標籤。整個完整的流程都是一個人做的。有很多個工人每天都在做這個事情。

老爸向小夥子訴苦,感覺招工挺多人的,生產力還是提不上去。小夥子記着老爸的話,在工廠裏面觀察了幾天,他發現每個工人都要做這 4 個打扮包裝布娃娃的工序,有些工人扎辮子很快但穿衣服很慢,有些工人扎辮子很慢但穿衣服快,他用了筆記本記下來:李大姨扎辮子快,王大媽穿衣服快,就這樣把每個人有效率的工作都記錄下來。

一天晚上吃飯,小夥子跟老爸說了自己觀察到的現象,也把本子拿給老爸看,跟老爸商量:可不可以做個嘗試,不要每個人負責打扮包裝布娃娃全步驟,而是按工序分開,每個人只負責一個工序,每個工人只幹一件事,更容易熟能生巧。老爸聽着覺得有道理。

第二天早上,就到小作坊裏,召集了所有工人,按小夥子的筆記上面的名單分工,大家都做好各自負責的內容,像流水線一樣,做好了就放到下個工序的地方,讓下個工序的人去做。到了下班,小夥子清點了今天工作的成果,包裝完成的娃娃比前一天多了 50% 。晚上小夥子跟老爸喝着百威吃起大肉慶祝一番。

這個故事你看了可能想罵爹罵娘,跟上面的定義有啥毛關係?故事只是把大家帶入這個場景,我們在工作中,着手開發之前不都得先理清好需求背景,這就是要講接口隔離原則的背景,通過代碼來給大家講解一下如何用好接口隔離原則。

父親的運營模式

先看代碼

interface Work {

    void hairBraiding();
    void getDressed();
    void packingIntoTheBox();
    void makeTag();

}

class WangMather implements Work{

    @Override
    public void hairBraiding() {
        System.out.println("王大媽給布娃娃扎辮子");
    }

    @Override
    public void getDressed() {
        System.out.println("王大媽給布娃娃穿衣服");
    }

    @Override
    public void packingIntoTheBox() {
        System.out.println("王大媽把布娃娃裝入箱子");
    }

    @Override
    public void makeTag() {
        System.out.println("王大媽給箱子打標籤");
    }
}

class LiAunt implements Work {

    @Override
    public void hairBraiding() {
        System.out.println("李大姨給布娃娃扎辮子");
    }

    @Override
    public void getDressed() {
        System.out.println("李大姨給布娃娃穿衣服");
    }

    @Override
    public void packingIntoTheBox() {
        System.out.println("李大姨把布娃娃裝入箱子");
    }

    @Override
    public void makeTag() {
        System.out.println("李大姨給箱子打標籤");
    }
}

// 測試代碼
WangMather wangMather = new WangMather();
wangMather.hairBraiding();
wangMather.getDressed();
wangMather.packingIntoTheBox();
wangMather.makeTag();

LiAunt liAunt = new LiAunt();
liAunt.hairBraiding();
liAunt.getDressed();
liAunt.packingIntoTheBox();
liAunt.makeTag();

在父親管理下的小作坊,是大家各自完成好一個布娃娃,工作互不交接,在這種運營模式下,我們把所有工作都合併在一個接口 Work 是沒有問題的。有人可能要問,不是說接口隔離麼?這裏面 Work 接口的 4 個方法都可以分離開,它們都是各自的工作內容。稍等一下,我們現在是基於老父親運營的模式下實現,如果小作坊一直都是這種模式運營,這段代碼有問題麼?其實沒問題的,我們根據當時的業務考慮,在這種情況下,把 Work 抽成 4 個接口不是不可以,只是不現實,每個工人都去實現一模一樣的 4 個接口在老父親運營模式下是不切實際。

兒子的運營模式

接下來介紹兒子的運營模式。兒子提倡的是每個工人職責分明,只負責一個事情,在這種情況下,如果還是用老父親的 Work 接口會有什麼問題呢?上面我們說了,李大姨扎辮子快,王大媽穿衣服快,所以李大姨被分配去給布娃娃扎辮子,王大媽被分配去給布娃娃穿衣服。我們沿用老父親的 Work 接口實現,代碼如下

class WangMather2 implements Work{

    @Override
    public void hairBraiding() {
    }

    @Override
    public void getDressed() {
        System.out.println("王大媽給布娃娃穿衣服");
    }

    @Override
    public void packingIntoTheBox() {
    }

    @Override
    public void makeTag() {
    }
}

class LiAunt2 implements Work {

    @Override
    public void hairBraiding() {
        System.out.println("李大姨給布娃娃扎辮子");
    }

    @Override
    public void getDressed() {
    }

    @Override
    public void packingIntoTheBox() {
    }

    @Override
    public void makeTag() {
    }
}

看出問題來了麼?李大姨僅僅參與扎辮子工作,王大媽參與了穿衣服工作,但是卻都要依舊實現其他 3 個多餘的接口。所以在兒子的運營模式下,老父親的 Work 接口需要重新分配,以工序的角度分配,而不是以完成一個布娃娃的角度分配。總共有 4 個工序:扎辮子、穿衣服、包裝入箱、打標籤,我們需要定義 4 個接口,讓員工去實現各自負責的工序接口。代碼如下


interface Hair {
    void hairBraiding();
}

interface Dress {
    void getDressed();
}

interface Box {
    void packingIntoTheBox();
}

interface Tag {
    void makeTag();
}

/**
 * 李大姨給布娃娃扎辮子快
 */
class LiAunt3 implements Hair {

    @Override
    public void hairBraiding() {
        System.out.println("李大姨給布娃娃扎辮子");
    }
}

/**
 * 王大媽給布娃娃穿衣服快
 */
class WangMather3 implements Dress{

    @Override
    public void getDressed() {
        System.out.println("王大媽給布娃娃穿衣服");
    }

}

/**
 * 陳大叔包裝快
 */
class ChenUncle implements Box {

    @Override
    public void packingIntoTheBox() {
        System.out.println("陳大叔給布娃娃裝箱");
    }
}

/**
 * 黃大姐貼標籤快
 */
class HuangSister implements Tag {

    @Override
    public void makeTag() {
        System.out.println("黃大姐給箱子打標籤");
    }
}

// 測試代碼
LiAunt3 liAunt3 = new LiAunt3();
WangMather3 wangMather3 = new WangMather3();
ChenUncle chenUncle = new ChenUncle();
HuangSister huangSister = new HuangSister();
liAunt3.hairBraiding();
wangMather3.getDressed();
chenUncle.packingIntoTheBox();
huangSister.makeTag();

這段代碼看起來就很清晰了,在兒子的運營模式下,大家都是隻做一道工序,這樣子實現就非常合理。看了這個過程,你理解了接口隔離原則了麼?再看一看上面的定義:客戶端不應該依賴它不需要的接口。閉上眼睛,靜默 3 秒,感受一下。
我們也可以回憶一下在工作中編寫的代碼,是不是有遵守接口隔離原則?在特定的場景下,如果很多類實現了同一個接口,並且都只實現了接口的極少部分方法,這時候很有可能就是接口隔離性不好,就要去分析能不能把方法拆分到不同的接口。

總結

接口隔離原則最最最重要一點就是要根據實際情況,具體業務具體分析,不能犯了上面說到的錯誤:在老父親的運營模式下,按兒子的工序劃分接口去實現,那樣子會得不償失。

希望文章對您有所幫助,設計模式系列會持續更新,感興趣的同學可以關注公衆號,第一時間獲取文章推送閱讀,也可以一起交流,交個朋友。

LieBrother

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章