什麼是介面和抽象類?
謹記:設計嚴謹的軟體重要的標準就是需要經的起測試,一個程式好不好被測試,測試發現問題能不能被良好的修復,程式狀況能否被監控,這都有賴於對抽象類和介面的正確使用。
介面和抽象類,是高階面向物件設計的起點。想要學習設計模式,必須有著對抽象類和介面的良好認知,和SOLID的認知,並在日常工作中正確的使用他們。
先簡述一下SOLID的特指的五種原則,優秀的設計模式,都是參考於這五種原則的實現;
SOLID:
SRP:Single Responsibility Principle 單一職責原則
OCP:Open Closed Principle 開閉原則
LSP:Liskov Substitution Principle 里氏替換原則
ISP:Interface Segregation Principle 介面隔離原則
DIP:Dependency Inversion Principle 依賴反轉原則
什麼是介面和抽象類:
- 介面和抽象類都是 “軟體工程產物”
- 具體類——抽象類——介面:越來越抽象,內部實現的東西越來越少
- 抽象類是未完全實現邏輯的類(可以有欄位和非public成員,他們代表了 “具體邏輯”)
- 抽象類為複用而生:專門作為基類來使用,也具有解耦的功能。
- 抽象類封裝的是確定的,開放的是不確定的,推遲到合適的子類中去實現。
- 介面是完全未實現邏輯的 “類”,(“純虛類”;只有函式成員;成員預設為public且不能為private) 但在新的C#版本中,介面也能擁有屬性,索引,事件,和方法的預設實現。
- 介面為解耦而生:“高內聚,底耦合”,方便單元測試。
- 介面是一個 “協約”,它規定你必須有什麼。
- 它們都不能例項化,只能用來宣告變數,引用具體類的例項。
為做基類而生的 “抽象類”與 “開放/關閉原則”: (所有的規則都是為了更好的協作)
我們應該封裝那些不變的,穩定的,確定的而把那些不確定的,有可能改變的成員宣告為抽象成員,並且留給子類去實現。
錯誤的功能封裝實現:違反了開閉原則,每當新增功能就會新增程式碼
class Program { static void Main(string[] args) { Vehicle vehicleView Code= new Car(); vehicle.Run("car"); } } class Vehicle { public void Stop() { Console.WriteLine("stopped!"); } public void Fill() { Console.WriteLine("Pay and fill..."); } public void Run(string type) { //這時候又來一輛車 我們又得加程式碼了 破壞了 開閉原則 switch (type) { case "car": Console.WriteLine("car is running..."); break; case "truck": Console.WriteLine("truck is running..."); break; default: break; } } } class Car : Vehicle { public void Run() { Console.WriteLine("car is running..."); } } class Truck : Vehicle { public void Run() { Console.WriteLine("truck is running..."); } }
利用多型的機制優化上述程式碼:
class Program { static void Main(string[] args) { Vehicle vehicle = new Car(); vehicle.Run(); } } class Vehicle { public void Stop() { Console.WriteLine("stopped!"); } public void Fill() { Console.WriteLine("Pay and fill..."); } public virtual void Run() { Console.WriteLine("vehicle is running..."); } } class Car : Vehicle { public override void Run() { Console.WriteLine("car is running..."); } } class Truck : Vehicle { public override void Run() { Console.WriteLine("truck is running..."); } }多型優化
我們可以發現vehicle的Run方法,我們基本上很少能用到,那我們去掉它的方法實現,一個虛方法卻沒有函式實現,這不就是純虛方法了嗎? 在C#中,純虛方法的替代型別是abstract。
在現在這個方法中,它封裝了確定的方法,開放了不確定的方法,符合了抽象類的設計,也遵循了開閉/原則。
class Program { static void Main(string[] args) { Vehicle vehicle = new Car(); vehicle.Run(); } } //封裝出了確定的方法 開放了不確定的方法 abstract class Vehicle { public void Stop() { Console.WriteLine("stopped!"); } public void Fill() { Console.WriteLine("Pay and fill..."); } public abstract void Run(); } class Car : Vehicle { public override void Run() { Console.WriteLine("car is running..."); } } class Truck : Vehicle { public override void Run() { Console.WriteLine("truck is running..."); } }抽象類優化
這個時候我們如果想要新增一個型別的汽車,只需要繼承並實現它的抽象方法即可,這更符合開閉/原則。
現在我們讓VehicleBase做為純抽象類,這在C#中是一種常見的模式,我們可以分“代”的來完成開放的不確定方法,讓方法慢慢變得清晰和確定。
class Program { static void Main(string[] args) { Vehicle vehicle = new Car(); vehicle.Run(); } } abstract class VehicleBase { public abstract void Stop(); public abstract void Fill(); public abstract void Run(); } abstract class Vehicle : VehicleBase { public override void Fill() { Console.WriteLine("pay and fill..."); } public override void Stop() { Console.WriteLine("stopped!"); } } class Car : Vehicle { public override void Run() { Console.WriteLine("car is running..."); } } class Truck : Vehicle { public override void Run() { Console.WriteLine("truck is running..."); } }純抽象類的優化
讓我們在思考下去,純抽象類,不就是介面的預設實現模式嗎,我們將純抽象類改成介面。
class Program { static void Main(string[] args) { Vehicle vehicle = new Car(); vehicle.Run(); } } interface IVehicle { void Stop(); void Fill(); void Run(); } abstract class Vehicle : IVehicle { public void Fill() { Console.WriteLine("pay and fill..."); } public void Stop() { Console.WriteLine("stopped!"); } abstract public void Run(); } class Car : Vehicle { public override void Run() { Console.WriteLine("car is running..."); } } class Truck : Vehicle { public override void Run() { Console.WriteLine("truck is running..."); } }介面的優化
這是不是就熟悉多了,這是一種良好的設計方法了。