設計模式-----接口隔離原則

1、原理介紹

一、官方定義

接口隔離原則,英文縮寫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。優化

官方翻譯:其一是不該該強行要求客戶端依賴於它們不用的接口;其二是類之間的依賴應該創建在最小的接口上面。簡單點說,客戶端須要什麼功能,就提供什麼接口,對於客戶端不須要的接口不該該強行要求其依賴;類之間的依賴應該創建在最小的接口上面,這裏最小的粒度取決於單一職責原則的劃分。翻譯

二、本身理解

2.一、原理解釋

  • 不該該強行要求客戶端依賴於它們不用的接口。語句很好理解,即客戶端須要什麼接口,就依賴什麼接口,不須要的就不依賴。那麼咱們反過來講,若是客戶端依賴了它們不須要的接口,那麼這些客戶端程序就面臨不須要的接口變動引發的客戶端變動的風險,這樣就會增長客戶端和接口之間的耦合程度,顯然與「高內聚、低耦合」的思想相矛盾。
  • 類之間的依賴應該創建在最小的接口上面。何爲最小的接口,即可以知足項目需求的類似功能做爲一個接口,這樣設計主要就是爲了「高內聚」。那麼咱們如何設計最小的接口呢?那就要說說粒度的劃分了,粒度細化的程度取決於咱們上一章講的的單一職責原則裏面接口劃分的粒度。從這一點來講,接口隔離和單一職責兩個原則有必定的類似性。

2.二、接口隔離原則和單一職責原則

從功能上來看,接口隔離和單一職責兩個原則具備必定的類似性。其實若是咱們仔細想一想仍是有區別的。設計

(1)從原則約束的側重點來講,接口隔離原則更關注的是接口依賴程度的隔離,更加關注接口的「高內聚」;而單一職責原則更加註重的是接口職責的劃分。code

(2)從接口的細化程度來講,單一職責原則對接口的劃分更加精細,而接口隔離原則注重的是相同功能的接口的隔離。接口隔離裏面的最小接口有時能夠是多個單一職責的公共接口。接口

(3)單一職責原則更加偏向對業務的約束,接口隔離原則更加偏向設計架構的約束。這個應該好理解,職責是根據業務功能來劃分的,因此單一原則更加偏向業務;而接口隔離更可能是爲了「高內聚」,偏向架構的設計。ip

2、場景示例

下面就以咱們傳統行業的訂單操做爲例來講明下接口隔離的必要性。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("訂單結束");
        }
    }

這樣設計就能完美解決上述「胖接口」致使的問題,若是須要增長訂單操做,只須要在對應的接口和實現類上面修改便可,這樣就不存在依賴不須要接口的狀況。經過這種設計,下降了單個接口的複雜度,使得接口的「內聚性」更高,「耦合性」更低。由此能夠看出接口隔離原則的必要性。

3、總結

經過以上訂單功能的優化,咱們看到了接口隔離原則的必要性,固然,關於接口隔離原則和單一職責原則的細節咱們也沒必要過多追究,無論何種原則,能解決咱們的設計問題就是好的原則、咱們必須遵照的原則。

相關文章
相關標籤/搜索