接口(interface)
抽象類中的抽象方法只規定了不能是 private 的,而接口中的“抽象方法”只能是 public 的。
這樣的成員訪問級別就決定了接口的本質:接口是服務消費者和服務提供者之間的契約。
既然是契約,那就必須是透明的,對雙方都是可見的。
除了 public,abstract 的抽象方法還可以是 protected 和 internal,它們都不是給功能調用者準備的,各自有特定的可見目標。
接口即契約(contract)
契約使自由合作成為可能,所謂自由合作就是一份合同擺在這里,它即約束服務的使用者也約束服務的提供者。如果該契約的使用者和提供者有多個,它們之間還能自由組合
示例推導
重載
同一個方法名被用于定義多個方法,但這些方法具有不同的參數列表(參數的數量、類型或參數的修飾符如ref、out、params等)。
當調用一個方法時,編譯器會根據提供的參數類型和數量來確定應該調用哪個版本的方法。
class Program{static void Main(string[] args){int[] nums1 = new int[] { 1, 2, 3, 4, 5 };ArrayList nums2 = new ArrayList { 1, 2, 3, 4, 5 };Console.WriteLine(Sum(nums1));Console.WriteLine(Sum(nums2));}// 計算和:該Sum()只能計算int類型的數組和// 不能計算nums2的object類型,需要用重載,兩個Sum()static int Sum(int[] nums){int sum = 0;foreach (var item in nums){ sum += item;}return sum;}static int Sum(ArrayList nums){int sum = 0;foreach (var item in nums){sum += (int)item; // 強制轉換為 int 類型}return sum;}}
接口版
int 整型數組的基類是 Array,F12可知其實現了接口 IEnumerable
ArrayList 也實現了接口 IEnumerable ,都遵守契約 IEnumerable
兩個方法只要求能被迭代即可,就可以把具體類型換成接口 IEnumerable
// 使用接口,只寫一次方法即可
static int Sum(IEnumerable nums){int sum = 0;foreach (var item in nums){sum += (int)item; // 強制轉換為 int 類型}return sum;}
依賴和耦合
現實中有分工、合作,面向對象是對現實的抽象,也有分工、合作。
在面向對象中,合作的專業術語叫“依賴”,依賴的同時就有了耦合,依賴越直接,耦合就越緊。
高內聚低耦合
內聚性:又稱塊內聯系。指模塊的功能強度的度量,即一個模塊內元素彼此之間結合的緊密程度的度量。若一個程序之間各元素之間(程序段之間)聯系緊密,則內聚性就高(高內聚)。
耦合性:又稱塊間聯系。指軟件系統各模塊之間相互緊密聯系程度的一種度量。模塊之間聯系越緊密,其耦合性就越強,模塊的獨立性就越差。相反其耦合性就越弱(低耦合)。
Car 和 Engine 的緊耦合示例
class Car與Engine緊耦合{static void Main(string[] args){var engine = new Engine();Car car = new Car(engine);car.Run(3);Console.WriteLine(car.Speed);}class Engine{public int PRM { get; set; } // 設置屬性“引擎轉數”public void Work(int gas) // gas:汽油{this.PRM = 1000 * gas;}}class Car{// Car 類里有 Engine 類型的字段,它倆就是緊耦合了// Car 依賴于 Engineprivate Engine _engine;public int Speed { get; set; }// // 當創建一個新的 Car 對象時,必須為這個構造函數提供一個 Engine 對象。public Car(Engine engine) {_engine = engine;}public void Run(int gas){_engine.Work(gas);this.Speed = _engine.PRM / 100; // 速度}}}
緊耦合的問題:
- 基礎類一旦出問題,上層類寫得再好也沒轍
- 程序調試時很難定位問題源頭
- 基礎類修改時,會影響寫上層類的其他程序員的工作
所以程序開發中要盡量避免緊耦合,解決方法就是接口。
接口:
- 約束調用者只能調用接口中包含的方法
- 讓調用者放心去調,不必關心方法怎么實現的、誰提供的
接口解耦示例
以老式手機舉例,對用戶來說他只關心手機可以接、打電話和收、發短信。
對于手機廠商,接口約束了他只要造的是手機,就必須可靠實現上面的四個功能。
用戶如果丟了個手機,他只要再買個手機,不必關心是那個牌子的,肯定也包含這四個功能,上手就可以用。用術語來說就是“人和手機是解耦的”。
class Program{static void Main(string[] args){}class PhoneUser{private Iphone _iphone;public PhoneUser(Iphone iphone){_iphone = iphone;}public void UsePhone(){_iphone.Dail();_iphone.PickUp();}}interface Iphone{void Dail(); // 打void PickUp(); // 接}class HuaWeiPhone:Iphone{public void Dail(){Console.WriteLine("HuaWei is calling");}public void PickUp(){Console.WriteLine("Hello!This is HuaWei");}}class XiaoMiPhone:Iphone{public void Dail(){Console.WriteLine("XiaoMi is calling");}public void PickUp(){Console.WriteLine("Hello!This is XiaoMi");}}}
- 沒有用接口時,如果一個類壞了,你需要 Open 它再去修改,修改時可能產生難以預料的副作用。引入接口后,耦合度大幅降低,換手機只需要換個類名,就可以了。
- 等學了反射后,連這里的一行代碼都不需要改,只要在配置文件中修改一個名字即可。
- 在代碼中只要有可以替換的地方,就一定有接口的存在;接口就是為了解耦(松耦合)而生。
- 松耦合最大的好處就是讓功能的提供方變得可替換,從而降低緊耦合時“功能的提供方不可替換”帶來的高風險和高成本。
高風險:功能提供方一旦出問題,依賴于它的功能都掛
高成本:如果功能提供方的程序員崩了,會導致功能使用方的整個團隊工作受阻