ISP = Interface Segregation Principle ISP的定義如下: 1、客戶端不應該依賴他不需要的介面 2、一個類對另外一個類的依賴性應該是建立在最小的介面上 3、不應當將不同的介面合併在一起,形成一個臃腫的大介面,這是對介面的污染 4、使用多個專門的介面要比使用單一的總 ...
ISP = Interface Segregation Principle ISP的定義如下: 1、客戶端不應該依賴他不需要的介面 2、一個類對另外一個類的依賴性應該是建立在最小的介面上 3、不應當將不同的介面合併在一起,形成一個臃腫的大介面,這是對介面的污染 4、使用多個專門的介面要比使用單一的總介面要好 ISP的幾個使用原則 1、根據介面隔離原則拆分介面時,首先必須滿足單一職責原則: 沒有哪個設計可以十全十美的考慮到所有的設計原則,有些設計原則之間就可能出現衝突,就如同單一職責原則和介面隔離原則,一個考慮的是介面的職責的單一性,一個考慮的是方法設計的專業性(儘可能的少),必然是會出現衝突。在出現衝突時,儘量以單一職責為主,當然這也要考慮具體的情況。 2、提高高內聚: 提高介面,類,模塊的處理能力,減少對外的交互。比如你給殺手提交了一個訂單,要求他在一周之內殺一個人,一周後殺手完成了任務,這種不講條件完成任務的表現就是高內聚。具體來說就是:要求在介面中儘量少公佈public方法,介面是對外的承諾,承諾越少對系統的開發越有利,變更的風險就越小,也有利於降低成本。 3、定製服務: 單獨為一個個體提供優良服務(只提供訪問者需要的方法)。 4、介面設計要有限度: 根據經驗判斷 介面隔離原則和單一職責原則就是一個硬幣的兩面,他們說的其實是一回事。 只是介面隔離原則是站在服務調用者角度看問題,單一職責原則是站在服務提供者的角度看。 =========================================================================================== 契約是怎麼回事呢? 契約就是在說兩件事,甲方在契約里說我不會多要,乙方會在契約里說我不會少給。 乙方的不會少給是比較容易做到的,因為當一個類實現一個介面的時候,他必須要實現介面里的所有方法。如果你不想實現所有的方法,你留下了抽象方法,那你這個類就是抽象類,不能被實例化,即你不是一個完整的服務提供者。所以說乙方不會少給,是強制性的。 甲方不會多要是軟性的規定,他是個設計上的東西,需要我們用一些設計原則去約束和控制大家寫代碼。因為編譯器是能檢測出乙方是不是少給,沒法檢查出來甲方是不是多要了。 那麼我怎麼知道甲方有沒有多要呢?很簡單,就看傳遞給調用者介面類型里,有沒有一直沒有被調用到的函數成員,如果有,就說明傳遞進來的介面類型太大了(太胖了),換句話說 胖介面就是這個介面是由兩個或兩個以上本質不同的小一點介面合併起來的,把大介面傳遞進來,只能一部分介面被調用到,另一部分就多餘出來了。 根據胖介面的產生原因不同,違反介面隔離原則可能帶來的不好的後果基本上有兩個: 1、第一種情況,設計的時候有問題,把太多的功能介面包含進來,那其中必然有一部分功能永遠用不到,也就自然違反了介面隔離原則。 我們看一下實例: 場景介紹: 一對小情侶,一天女生給男生打電話,告訴他車被追尾了,哭的梨花帶雨。小男生情商高,哄小女生說 不要緊,明天給你買輛坦克,就不怕追尾了。(前提是小女生不能開炮,只能開~~~~) 第一版的實現=》小女生只會開汽車,不會開別的
#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(); } }
服務調用方:
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!!!!!!"); } }
駕駛員類:
class Driver { private IVehicle _vehicle; public Driver(IVehicle vehicle) { _vehicle = vehicle; } public void Drive() { _vehicle.Run(); } }
服務調用方:
//介面隔離的原則是 服務的調用方不會都要 //本例子中服務的調用方的需求很簡單,這是要求會run,不要求fire //因此原先的ITank介面中自己包含的fire和run就符合胖介面的規則,他提供了多餘的介面給調用方 //因此把ITank介面隔離開是對的 var driver = new Driver(new HeavyTank());//開坦克 driver.Drive(); driver = new Driver(new Car());//開汽車 driver.Drive(); Console.ReadKey();
第二種情況明天繼續,哇哈哈~~~~~~~~~~