個人博客原文: "介面隔離原則" 設計模式六大原則之四:介面隔離原則。 簡介 姓名 :介面隔離原則 英文名 :Interface Segregation Principle 價值觀 :寧缺毋濫 個人介紹 : 1. Clients should not be forced to depend upon ...
個人博客原文:
介面隔離原則
設計模式六大原則之四:介面隔離原則。
簡介
姓名 :介面隔離原則
英文名 :Interface Segregation Principle
價值觀 :寧缺毋濫
個人介紹 :
- Clients should not be forced to depend upon interfaces that they don't use.(客戶端不應該依賴它不需要的介面。)
- 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 秒,感受一下。
我們也可以回憶一下在工作中編寫的代碼,是不是有遵守介面隔離原則?在特定的場景下,如果很多類實現了同一個介面,並且都只實現了介面的極少部分方法,這時候很有可能就是介面隔離性不好,就要去分析能不能把方法拆分到不同的介面。
總結
介面隔離原則最最最重要一點就是要根據實際情況,具體業務具體分析,不能犯了上面說到的錯誤:在老父親的運營模式下,按兒子的工序劃分介面去實現,那樣子會得不償失。
參考資料:《大話設計模式》、《Java設計模式》、《設計模式之禪》、《研磨設計模式》、《Head First 設計模式》
希望文章對您有所幫助,設計模式系列會持續更新,感興趣的同學可以關註公眾號,第一時間獲取文章推送閱讀,也可以一起交流,交個朋友。