面向對象的六大原則之 接口隔離原則——ISP

ISP = Interface Segregation Principle
 
ISP的定義以下:
一、客戶端不該該依賴他不須要的接口
二、一個類對另一個類的依賴性應該是創建在最小的接口上
三、不該當將不一樣的接口合併在一塊兒,造成一個臃腫的大接口,這是對接口的污染
四、使用多個專門的接口要比使用單一的總接口要好
 
 
ISP的幾個使用原則
一、根據接口隔離原則拆分接口時,首先必須知足單一職責原則: 沒有哪一個設計能夠十全十美的考慮到全部的設計原則,有些設計原則之間就可能出現衝突,就如同單一職責原則和接口隔離原則,一個考慮的是接口的職責的單一性,一個考慮的是方法設計的專業性(儘量的少),必然是會出現衝突。在出現衝突時,儘可能以單一職責爲主,固然這也要考慮具體的狀況。
二、提升高內聚: 提升接口,類,模塊的處理能力,減小對外的交互。好比你給殺手提交了一個訂單,要求他在一週以內殺一我的,一週後殺手完成了任務,這種不講條件完成任務的表現就是高內聚。具體來講就是:要求在接口中儘可能少公佈public方法,接口是對外的承諾,承諾越少對系統的開發越有利,變動的風險就越小,也有利於下降成本。
三、定製服務: 單獨爲一個個體提供優良服務(只提供訪問者須要的方法)。
四、接口設計要有限度: 根據經驗判斷
 
 
接口隔離原則和單一職責原則就是一個硬幣的兩面,他們說的實際上是一回事。
只是接口隔離原則是站在服務調用者角度看問題,單一職責原則是站在服務提供者的角度看。
 
 
===========================================================================================
契約是怎麼回事呢?
契約就是在說兩件事,甲方在契約裏說我不會多要,乙方會在契約裏說我不會少給。
乙方的不會少給是比較容易作到的,由於當一個類實現一個接口的時候,他必需要實現接口裏的全部方法。若是你不想實現全部的方法,你留下了抽象方法,那你這個類就是抽象類,不能被實例化,即你不是一個完整的服務提供者。因此說乙方不會少給,是強制性的。
甲方不會多要是軟性的規定,他是個設計上的東西,須要咱們用一些設計原則去約束和控制你們寫代碼。由於編譯器是能檢測出乙方是否是少給,無法檢查出來甲方是否是多要了。
 
那麼我怎麼知道甲方有沒有多要呢?很簡單,就看傳遞給調用者接口類型裏,有沒有一直沒有被調用到的函數成員,若是有,就說明傳遞進來的接口類型太大了(太胖了),換句話說 胖接口就是這個接口是由兩個或兩個以上本質不一樣的小一點接口合併起來的,把大接口傳遞進來,只能一部分接口被調用到,另外一部分就多餘出來了。
根據胖接口的產生緣由不一樣,違反接口隔離原則可能帶來的很差的後果基本上有兩個:
一、第一種狀況,設計的時候有問題,把太多的功能接口包含進來,那其中必然有一部分功能永遠用不到,也就天然違反了接口隔離原則。
  咱們看一下實例:
   場景介紹: 一對小情侶,一天女生給男生打電話,告訴他車被追尾了,哭的梨花帶雨。小男生情商高,哄小女生說 沒關係,明天給你買輛坦克,就不怕追尾了。(前提是小女生不能開炮,只能開~~~~)
 
 
   初版的實現=》小女生只會開汽車,不會開別的
  
#region 車輛接口和實現

    interface IVehicle
    {
        void Run();
    }

    class Car : IVehicle
    {
        public void Run()
        {
            Console.WriteLine("Car is Running");
        }
    }
    class Truck : IVehicle
    {
        public void Run()
        {
            Console.WriteLine("Truck is Running");
        }
    }

    #endregion

  駕駛員類:函數

class Driver
    {
        private IVehicle _vehicle;
        public Driver(IVehicle vehicle)
        {
            _vehicle = vehicle;
        }
        public void Drive()
        {
            _vehicle.Run();
        }
    }

服務調用方:spa

   var driver = new Driver(new Car());//開汽車
            driver = new Driver(new Truck());//開卡車
            driver.Drive();

            //這時候你會發現,若是小女生想要開坦克的話,目前是知足不了的
            //由於Driver構造參數傳遞的是IVehicle接口,不是ITank接口
            //若是想要知足小女生開坦克上街的願望,就必須改造Driver,傳遞ITank接口,請看下一個例子

            Console.ReadKey();

  第二版的實現=》小女生能開坦克,可是卻不能開汽車了設計

      

 class Driver
    {
        private ITank _tank;
        public Driver(ITank tank)
        {
            _tank = tank;
        }
        public void Drive()
        {
            _tank.Run();
        }
    }
   var driver = new Driver(new HeavyTank());//開坦克
            driver.Drive();

            // 這時候你會發現, 小女生能開坦克上街了,可是你又會發現,小女生如今只會開坦克了,不會開車了
            // 問題出如今哪裏呢?
            // 咱們把一個胖接口(ITank)傳遞進來,這個胖接口中有一個咱們永遠用不到的功能,就是fire。
            // 因此如今這個設計是違反了接口隔離原則
            // 具體改造請看下一個例子

            Console.ReadKey();

 

   第三版的實現=》符合接口隔離原則,小女生能開坦克,也能開汽車了。
#region 車輛接口和實現

    interface IVehicle
    {
        void Run();
    }

    class Car : IVehicle
    {
        public void Run()
        {
            Console.WriteLine("Car is Running");
        }
    }
    class Truck : IVehicle
    {
        public void Run()
        {
            Console.WriteLine("Truck is Running");
        }
    }

    #endregion
interface IWeapon
    {
        void Fire();
    }
  interface ITank:IVehicle,IWeapon
    {
    }
    class LightTank : ITank
    {
        public void Fire()
        {
            Console.WriteLine("Boom!");
        }

        public void Run()
        {
            Console.WriteLine("Ka Ka Ka!");
        }
    }

    class HeavyTank : ITank
    {
        public void Fire()
        {
            Console.WriteLine("Boom!!!!!!!!");
        }

        public void Run()
        {
            Console.WriteLine("Ka!!! Ka!!!! Ka!!!!!!");
        }
    }

駕駛員類:3d

  class Driver
    {
        private IVehicle _vehicle;
        public Driver(IVehicle vehicle)
        {
            _vehicle = vehicle;
        }
        public void Drive()
        {
            _vehicle.Run();
        }
    }

服務調用方:code

      //接口隔離的原則是 服務的調用方不會都要
            //本例子中服務的調用方的需求很簡單,這是要求會run,不要求fire
            //所以原先的ITank接口中本身包含的fire和run就符合胖接口的規則,他提供了多餘的接口給調用方
            //所以把ITank接口隔離開是對的
            var driver = new Driver(new HeavyTank());//開坦克
            driver.Drive();
            driver = new Driver(new Car());//開汽車
            driver.Drive();


            Console.ReadKey();

 

 第二種狀況明天繼續,哇哈哈~~~~~~~~~~
相關文章
相關標籤/搜索