接口隔離原則,英文縮寫ISP,全稱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。優化
官方翻譯:其一是不該該強行要求客戶端依賴於它們不用的接口;其二是類之間的依賴應該創建在最小的接口上面。簡單點說,客戶端須要什麼功能,就提供什麼接口,對於客戶端不須要的接口不該該強行要求其依賴;類之間的依賴應該創建在最小的接口上面,這裏最小的粒度取決於單一職責原則的劃分。翻譯
從功能上來看,接口隔離和單一職責兩個原則具備必定的類似性。其實若是咱們仔細想一想仍是有區別的。設計
(1)從原則約束的側重點來講,接口隔離原則更關注的是接口依賴程度的隔離,更加關注接口的「高內聚」;而單一職責原則更加註重的是接口職責的劃分。code
(2)從接口的細化程度來講,單一職責原則對接口的劃分更加精細,而接口隔離原則注重的是相同功能的接口的隔離。接口隔離裏面的最小接口有時能夠是多個單一職責的公共接口。接口
(3)單一職責原則更加偏向對業務的約束,接口隔離原則更加偏向設計架構的約束。這個應該好理解,職責是根據業務功能來劃分的,因此單一原則更加偏向業務;而接口隔離更可能是爲了「高內聚」,偏向架構的設計。ip
下面就以咱們傳統行業的訂單操做爲例來講明下接口隔離的必要性。ci
軟件設計最初,咱們的想法是相同功能的方法放在同一個接口裏面,以下,全部訂單的操做都放在訂單接口IOrder裏面。理論上來講,這貌似沒錯。咱們來看看如何設計。get
public interface IOrder { //訂單申請操做 void Apply(object order); //訂單審覈操做 void Approve(object order); //訂單結束操做 void End(object order); }
剛開始只有銷售訂單,咱們只須要實現這個接口就行了。it
public class SaleOrder:IOrder { public void Apply(object order) { throw new NotImplementedException(); } public void Approve(object order) { throw new NotImplementedException(); } public void End(object order) { throw new NotImplementedException(); } }
後來,隨着系統的不斷擴展,咱們須要加入生產訂單,生產訂單也有一些單獨的接口方法,好比:排產、凍結、導入、導出等操做。因而咱們向訂單的接口裏面繼續加入這些方法。因而訂單的接口變成這樣:
public interface IOrder { //訂單申請操做 void Apply(object order); //訂單審覈操做 void Approve(object order); //訂單結束操做 void End(object order); //訂單下發操做 void PlantProduct(object order); //訂單凍結操做 void Hold(object order); //訂單刪除操做 void Delete(object order); //訂單導入操做 void Import(); //訂單導出操做 void Export(); }
咱們生產訂單的實現類以下
//生產訂單實現類 public class ProduceOrder : IOrder { /// <summary> /// 對於生產訂單來講無用的接口 /// </summary> /// <param name="order"></param> public void Apply(object order) { throw new NotImplementedException(); } /// <summary> /// 對於生產訂單來講無用的接口 /// </summary> /// <param name="order"></param> public void Approve(object order) { throw new NotImplementedException(); } /// <summary> /// 對於生產訂單來講無用的接口 /// </summary> /// <param name="order"></param> public void End(object order) { throw new NotImplementedException(); } public void PlantProduct(object order) { Console.WriteLine("訂單下發排產"); } public void Hold(object order) { Console.WriteLine("訂單凍結"); } public void Delete(object order) { Console.WriteLine("訂單刪除"); } public void Import() { Console.WriteLine("訂單導入"); } public void Export() { Console.WriteLine("訂單導出"); } }
銷售訂單的實現類也要相應作修改
//銷售訂單實現類 public class SaleOrder:IOrder { public void Apply(object order) { Console.WriteLine("訂單申請"); } public void Approve(object order) { Console.WriteLine("訂單審覈處理"); } public void End(object order) { Console.WriteLine("訂單結束"); } #region 對於銷售訂單無用的接口方法 public void PlantProduct(object order) { throw new NotImplementedException(); } public void Hold(object order) { throw new NotImplementedException(); } public void Delete(object order) { throw new NotImplementedException(); } public void Import() { throw new NotImplementedException(); } public void Export() { throw new NotImplementedException(); } #endregion }
需求作完了,上線正常運行。貌似問題也不大。系統運行一段時間以後,新的需求變動來了,要求生成訂單須要一個訂單撤銷排產的功能,那麼咱們的接口是否是就得增長一個訂單撤排的接口方法CancelProduct。因而乎接口變成這樣:
public interface IOrder { //訂單申請操做 void Apply(object order); //訂單審覈操做 void Approve(object order); //訂單結束操做 void End(object order); //訂單下發操做 void PlantProduct(object order); //訂單撤排操做 void CancelProduct(object order); //訂單凍結操做 void Hold(object order); //訂單刪除操做 void Delete(object order); //訂單導入操做 void Import(); //訂單導出操做 void Export(); }
這個時候問題就來了,咱們的生產訂單隻要實現這個撤銷的接口貌似就OK了,可是咱們的銷售訂單呢,原本銷售訂單這一塊咱們不想作任何的變動,但是因爲咱們IOrder接口裏面增長了一個方法,銷售訂單的實現類是否是也必需要實現一個無效的接口方法?這就是咱們常說的「胖接口」致使的問題。因爲接口過「胖」,每個實現類依賴了它們不須要的接口,使得層與層之間的耦合度增長,結果致使了不須要的接口發生變化時,實現類也不得不相應的發生改變。這裏就凸顯了咱們接口隔離原則的必要性,下面咱們就來看看如何經過接口隔離來解決上述問題。
咱們將IOrder接口分紅兩個接口來設計
//刪除訂單接口 public interface IProductOrder { //訂單下發操做 void PlantProduct(object order); //訂單撤排操做 void CancelProduct(object order); //訂單凍結操做 void Hold(object order); //訂單刪除操做 void Delete(object order); //訂單導入操做 void Import(); //訂單導出操做 void Export(); } //銷售訂單接口 public interface ISaleOrder { //訂單申請操做 void Apply(object order); //訂單審覈操做 void Approve(object order); //訂單結束操做 void End(object order); }
對應的實現類只須要實現本身須要的接口便可
//生產訂單實現類 public class ProduceOrder : IProductOrder { public void PlantProduct(object order) { Console.WriteLine("訂單下發排產"); } public void CancelProduct(object order) { Console.WriteLine("訂單撤排"); } public void Hold(object order) { Console.WriteLine("訂單凍結"); } public void Delete(object order) { Console.WriteLine("訂單刪除"); } public void Import() { Console.WriteLine("訂單導入"); } public void Export() { Console.WriteLine("訂單導出"); } } //銷售訂單實現類 public class SaleOrder : ISaleOrder { public void Apply(object order) { Console.WriteLine("訂單申請"); } public void Approve(object order) { Console.WriteLine("訂單審覈處理"); } public void End(object order) { Console.WriteLine("訂單結束"); } }
這樣設計就能完美解決上述「胖接口」致使的問題,若是須要增長訂單操做,只須要在對應的接口和實現類上面修改便可,這樣就不存在依賴不須要接口的狀況。經過這種設計,下降了單個接口的複雜度,使得接口的「內聚性」更高,「耦合性」更低。由此能夠看出接口隔離原則的必要性。
經過以上訂單功能的優化,咱們看到了接口隔離原則的必要性,固然,關於接口隔離原則和單一職責原則的細節咱們也沒必要過多追究,無論何種原則,能解決咱們的設計問題就是好的原則、咱們必須遵照的原則。