接口隔離模式
在講接口隔離原則之前,我們先明確一下我們的主角,什麽是接口,接口分為兩種:
一種是實例接口 (Object Interface),在 Java 中聲明一個類,然後用 new 關鍵字產生的一個實例,它是對一個類型的事 物描述,這是一種接口,比如你定義個 Person 這個類,然後使用 Person zhangSan = new Person()產生了 一個實例,這個實例要遵從的標準就是 Person 這個類,Person 類就是 zhangSan 的接口,看不懂?不要緊, 那是讓 Java 語言浸染的時間太長了。主角已經出場了,那我們看它的原則是什麽,它有兩種定義:
第一種定義: 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。類間的依賴關系應該建立在最小的接口上。
一個新事物的定義一般都是比較難理解的,晦澀難懂是正常的,否則那會讓人家覺的你沒有水平,這 也是一些國際廠商在國內忽悠的基礎,不整些名詞怎麽能讓你崇拜我呢?我們把這個定義剖析一下,先說 第一種定義客戶端不應該依賴它不需要接口,那依賴什麽?依賴它需要的接口,客戶端需要什麽接口就提 供什麽借口,把不需要的接口剔除掉,那就需要對接口進行細化,保證其純潔性;再看第二個定義,類間 的依賴關系應該建立在最小的接口上,它要求是最小的接口,也是要求接口細化,接口純潔,與第一個定 義如出一轍,只是一個事物的兩種不同描述。
我們可以把這兩個定義概括為一句話:建立單一接口,不要建立臃腫龐大的接口。再通俗的一點講: 接口盡量細化,同時接口中的方法盡量的少。看到這裏大家有可能要疑惑了,這與單一職責原則不是相同 的嗎?錯,接口隔離原則與單一職責的定義的規則是不相同的,單一職責要求的是類和接口職責單一,註 重的是職責,沒有要求接口的方法減少,例如一個職責可能包含 10 個方法,這 10 個方法都放在一個接口 中,並且提供給多個模塊訪問,各個模塊按照規定的權限來訪問,在系統外通過文檔約束不使用的方法不 要訪問,按照單一職責原則是允許的,按照接口隔離原則是不允許的,因為它要求“盡量使用多個專門的 接口”,專門的接口指什麽?就是指提供給多個模塊的接口,提供給幾個模塊就應該有幾個接口,而不是建 立一個龐大的臃腫的接口,所有的模塊可以來訪問。
未遵循接口隔離原則的設計:
遵循接口隔離原則的設計:
根據接口隔離原則拆分接口時,必須首先滿足單一職責原則。
我們來舉個例子來說明接口隔離原則到底對我們提出了什麽要求。現在男生對小姑娘的稱呼使用頻率 最高的應該是“美女”了吧,我們今天來定義一下什麽是美女:首先要面貌好看,其次是身材要窈窕,然 後要有氣質,當然了,這三者各人的排列順序不一樣,總之要成為一名美女就必須具備:面貌、身材和氣質,我們用類圖類體現一下星探(當然,你也可以把你自己想想成星探)找美女的過程,看類圖:
定義了一個 IPettyGirl 接口,聲明所有的美女都應該有 goodLooking、niceFigure 和 greatTemperament,然後又定義了一個抽象類 AbstractSearcher,其作用就是搜索美女然後展示信息,只 要美女都是按照這個規範定義,Searcher(星探)就輕松的多了,我們先來看美女的定義:
public interface IPettyGirl { //要有姣好的面孔 public void goodLooking(); //要有好身材 public void niceFigure(); //要有氣質 public void greatTemperament(); }
然後我們看美女的實現類:
public class PettyGirl implements IPettyGirl { private String name; //美女都有名字 public PettyGirl(String _name){ this. name=_name; } //臉蛋漂亮 public void goodLooking() { System. out.println(this. name + "---臉蛋很漂亮!"); } //氣質要好 public void greatTemperament() { System. out.println(this. name + "---氣質非常好!"); } //身材要好 public void niceFigure() { System. out.println(this. name + "---身材非常棒!"); } }
然後我們來看 AbstractSearcher 類,這個類一般就是指星探這個行業了,源代碼如下:
public abstract class AbstractSearcher { protected IPettyGirl pettyGirl; public AbstractSearcher(IPettyGirl _pettyGirl){ this.pettyGirl = _pettyGirl; } //搜索美女,列出美女信息 public abstract void show(); }
場景中的兩個角色美女和星探都已經完成了,我們再來寫個場景類,展示一下我們的這個過程:
public class Client { //搜索並展示美女信息 public static void main(String[] args) { //定義一個美女 IPettyGirl yanYan = new PettyGirl(" 嫣嫣"); AbstractSearcher searcher = new Searcher(yanYan); searcher.show(); } }
星探查找到美女,打印出美女的信息,源碼如下:
public class Searcher extends AbstractSearcher{ public Searcher(IPettyGirl _pettyGirl){ super(_pettyGirl); } //展示美女的信息 public void show(){ System. out.println("--------美女的信息如下: ---------------"); //展示面容 super. pettyGirl.goodLooking(); //展示身材 super. pettyGirl.niceFigure(); //展示氣質 super. pettyGirl.greatTemperament(); } }
運行結果如下:
--------美女的信息如下:---------------
嫣嫣---臉蛋很漂亮!
嫣嫣---身材非常棒!
嫣嫣---氣質非常好!
采用接口隔離原則:重新修改一下類圖:
把原 IPettyGirl 接口拆分為兩個接口,一種是外形美的美女 IGoodBodyGirl,這類美女的特點就是臉 蛋和身材極棒,超一流,但是沒有審美素質,比如隨地吐痰,出口就是 KAO,CAO 之類的,文化程度比較低; 另外一種是氣質美的美女 IGreatTemperamentGirl,談吐和修養都非常高。我們從一個比較臃腫的接口拆分 成了兩個專門的接口,靈活性提高了,可維護性也增加了,不管以後是要外形美的美女還是氣質美的美女 都可以輕松的通過 PettyGirl 定義。我們先看兩種類型的美女接口:
public interface IGoodBodyGirl { //要有姣好的面孔 public void goodLooking(); //要有好身材 public void niceFigure(); } public interface IGreatTemperamentGirl { //要有氣質 public void greatTemperament(); }
實現類沒有改變,只是實現類兩個接口,源碼如下:
private String name; //美女都有名字 public PettyGirl(String _name){ this. name=_name; } //臉蛋漂亮 public void goodLooking() { System. out.println(this. name + "---臉蛋很漂亮!"); } //氣質要好 public void greatTemperament() { System. out.println(this. name + "---氣質非常好!"); } //身材要好 public void niceFigure() { System. out.println(this. name + "---身材非常棒!"); } }
總結:
通過這樣的改造以後,不管以後是要氣質美女還是要外形美女,都可以保持接口的穩定。當然你可能 要說了,以後可能審美觀點再發生改變,只有臉蛋好看就是美女,那這個 IGoodBody 接口還是要修改的呀, 確實是,但是設計時有限度的,不能無限的考慮未來的變更情況,否則就會陷入設計的泥潭中而不能自拔。 以上把一個臃腫的接口變更為兩個獨立的接口依賴的原則就是接口隔離原則,讓 AbstractSearcher 依 賴兩個專用的接口比依賴一個綜合的接口要靈活。接口是我們設計時對外提供的契約,通過分散定義多個 接口,可以預防未來變更的擴散,提高系統的靈活性和可維護性。
接口隔離模式