前言
在理解這個概念以前,抽象跟虛擬對最初是初學者的我來說很難分辨,所以特別來描述關於這兩個的觀念。
抽象方法是一種只有方法的名稱和簽名(參數列表),但沒有實際方法內容的方法。
它是一個純粹的規範,告訴子類別(繼承它的類別)必須實作這個方法,但並不提供具體的實作方式。這樣的方法通常用關鍵字 abstract
來宣告,並且不包含方法主體(即方法內的程式碼)。
抽象方法的主要目的是為了讓子類別提供自己的實作,以滿足特定的需求。
換句話說,抽象方法建立了一個契約,要求所有繼承該抽象類別的子類別都必須提供自己的版本,以符合這個契約。
抽象類別是包含了抽象方法的類別,而且抽象類別本身也可以包含具體的方法。與普通類別不同,抽象類別不能直接實例化為物件,只能被用作其他類別的基礎,也就是被繼承。
抽象類別通常用於定義一組相關的類別所需實作的方法,但這些方法的實際內容會因為每個具體的子類別而有所不同。換句話說,抽象類別提供了一個通用的框架,並要求子類別根據自己的需求來填充這個框架。
抽象類別的特徵包括:
抽象方法和抽象類別是用來建立一個規範或契約,以確保衍生類別提供特定功能的程式設計概念。
鼓勵程式碼的重複使用和規範性,同時也允許繼承者自由實作具體的細節,從而實現多型性和更好的程式設計結構。
現在可以想像你有一個大筆記本,這個筆記本是特別的,因為它的每一頁都有一個不同的圖畫,但這些圖畫都是未完成的。而筆記本的第一頁可能是一個未完成的車子,第二頁可能是一個未完成的房子,以此類推。
這本筆記本就像是一個抽象類別,每一頁就像是一個抽象方法。抽象方法就像是一張空白的畫,上面只有一些輪廓和線條,但還沒有被填色或完成。
你知道每一頁都必須被填色才能變成一幅完整的畫,但這本筆記本本身不能被展示或使用,因為它只是一堆未完成的畫面的集合。
那如果你想要創建一個新的畫,可以複製這本筆記本的一頁,然後在上面填色,讓它變成一幅獨特的畫。這就像是創建一個繼承自抽象類別的新類別,並實作抽象方法,讓它變成一個具體的類別。
總之,抽象類別就像是一本未完成的筆記本,其中每一頁都是未完成的畫。你需要從這本筆記本中複製一頁,然後填色,才能創建一幅完整的畫。這樣,小孩可以更容易地理解抽象類別的概念。
當使用抽象類別時,可以確保子類別實現特定的成員,示範如何使用抽象類別:
範例 1:
原始程式碼:
public class Storage
{
public virtual void Save(string content)
{
// 存檔邏輯
}
}
public class FileStorage : Storage { }
public class DatabaseStorage : Storage { }
修改後的程式碼,使用抽象方法,可以看到關鍵字abstract :
public abstract class Storage
{
public abstract void Save(string content);
}
public class FileStorage : Storage
{
public override void Save(string content)
{
// 存成實體檔案的邏輯
}
}
public class DatabaseStorage : Storage
{
public override void Save(string content)
{
// 存入資料庫的邏輯
}
}
這樣,您可以確保任何繼承自 Storage
的子類別都必須實作 Save
方法,否則編譯將無法通過。
抽象類別(Storage
)以及它的兩個子類別(FileStorage
和 DatabaseStorage
),用於儲存資料的不同方式。以下是每個部分的詳細解釋:
Storage
抽象類別:
Storage
是一個抽象類別,使用 abstract
關鍵字來標記,表示它不能被實例化,只能用作其他類別的父類別。Save(string content)
,這個方法沒有實際的程式碼實作,只有方法的簽名(名稱和參數)。派生類別必須實作這個方法。FileStorage
類別:
FileStorage
是 Storage
抽象類別的一個子類別,使用 :
符號來指定它繼承自 Storage
。Save(string content)
方法,具體定義了將資料存儲為實體檔案的邏輯。也就是說,當您使用 FileStorage
類別的實例呼叫 Save
方法時,它會將提供的內容保存到檔案中。DatabaseStorage
類別:
DatabaseStorage
也是 Storage
抽象類別的一個派生類別,同樣使用 :
來繼承 Storage
。Save(string content)
方法,但這次的實作是將提供的內容存入資料庫,而不是儲存為實體檔案。這種結構允許您創建一個通用的 Storage
抽象類別,定義了資料儲存的通用界面,但不指定實際的儲存細節。然後,您可以創建具體的儲存方式,如 FileStorage
和 DatabaseStorage
,分別實作了 Save
方法以處理不同的儲存需求。這樣的設計使得程式碼具有彈性,可以根據需要輕鬆擴展和添加新的儲存方式。
範例 2:
原始程式碼:
public class Program
{
static void Main(string[] args)
{
Storage obj = new Storage();
obj.Save("hello world");
}
}
public class Storage
{
public void Save(string content)
{
// 存檔邏輯
}
}
修改後的程式碼,使用抽象方法:
public class Program
{
static void Main(string[] args)
{
Storage obj = new FileStorage();
obj.Save("hello world");
}
}
public abstract class Storage
{
public abstract void Save(string content);
}
public class FileStorage : Storage
{
public override void Save(string content)
{
// 存成實體檔案的邏輯
}
}
在範例中,Storage
類別變成抽象類別,並且 Save
方法變成抽象方法,要求所有繼承的子類別實作它。並且,在 Main
方法中,我們實例化了 FileStorage
類別,並呼叫其 Save
方法,這樣您就可以在子類別中實作不同的存檔邏輯。
使用抽象類別和抽象方法可以讓您更清晰地定義規範,確保子類別遵守這些規範,並提供一致的介面,以便使用者或其他開發人員更容易理解和使用您的程式碼。
抽象方法(abstract
method)和虛擬方法(virtual
method)都是用於實現多型性(polymorphism)的概念,但它們在某些重要方面有一些不同之處。下面描述關於抽象方法和虛擬方法之間的主要區別:
abstract
關鍵字聲明。抽象方法必須存在於抽象類別中,並且要求所有派生類別提供自己的具體實作。抽象方法強制衍生類別提供實作,以滿足特定的需求。virtual
關鍵字聲明,並在基底類別中提供默認實作。派生類別可以選擇性地覆寫虛擬方法,以修改或擴展其行為。如果不覆寫,將使用基底類別中的默認實作。抽象方法主要用於強制子類別提供特定的實作存在於抽象類別中,而虛擬方法則用於允許子類別選擇性地修改或擴展基底類別的行為,存在於可以實例化的類別中。
選擇使用哪種方法取決於您的設計需求,以及是否需要確保一致性和實作的強制性。
那就舉例用形狀(Shape)和它具體子類別,像是矩形(Rectangle)和圓形(Circle),來詳細說明抽象方法和虛擬方法之間的區別。
首先,創建一個抽象類別 Shape
,它包含一個抽象方法 CalculateArea
,方法名稱如同其命名,用於計算形狀的面積。
public abstract class Shape
{
public abstract double CalculateArea();
}
上面有一個抽象類別 Shape
,裡面有一個抽象方法 CalculateArea
,所有繼承 Shape
的子類別都必須實作這個方法。這就是抽象方法的特點,它強制子類別提供特定的實作。
現在,讓我們創建具體的子類別 Rectangle
和 Circle
,這些子類別將繼承 Shape
並實作 CalculateArea
方法。
public class Rectangle : Shape
{
public double Width { get; set; }
public double Height { get; set; }
public Rectangle(double width, double height)
{
Width = width;
Height = height;
}
public override double CalculateArea()
{
return Width * Height;
}
}
public class Circle : Shape
{
public double Radius { get; set; }
public Circle(double radius)
{
Radius = radius;
}
public override double CalculateArea()
{
return Math.PI * Radius * Radius;
}
}
Rectangle 和 Circle 類別都繼承自 Shape 並實作了 CalculateArea 方法,用來計算矩形和圓形的面積。
繼承之後,就可以開始創建這些形狀的實例,並呼叫 CalculateArea 方法來計算它們的面積:
static void Main()
{
Shape rectangle = new Rectangle(5, 4);
Shape circle = new Circle(3);
Console.WriteLine($"Rectangle Area: {rectangle.CalculateArea()}");
Console.WriteLine($"Circle Area: {circle.CalculateArea()}");
}
上面主要是使用抽象方法的使用。
在 Shape 基底類別中定義了一個抽象方法 CalculateArea ,子類別 Rectangle 和 Circle 必須實作這個方法,以提供具體的面積計算。
抽象的完成以後換來看看如果使用虛擬方法的話。
在這個情境下,我們修改父類別 Shape ,讓 CalculateArea 方法變成虛擬方法,而不是抽象方法,如下。
public class Shape
{
public virtual double CalculateArea()
{
return 0.0; // 默認面積值
}
}
這時候上面的 Shape 類別中的 CalculateArea 方法是虛擬的,並提供了一個默認的面積值(0.0)。這表示我們的子類別可以選擇性地覆寫這個方法,而不是強制性地提供實作。
再來看下面的 Rectangle 和 Circle 子類別,因為上面的方法是虛擬的,所以現在不再需要 override 關鍵字:
public class Rectangle : Shape
{
public double Width { get; set; }
public double Height { get; set; }
public Rectangle(double width, double height)
{
Width = width;
Height = height;
}
public override double CalculateArea()
{
return Width * Height;
}
}
public class Circle : Shape
{
public double Radius { get; set; }
public Circle(double radius)
{
Radius = radius;
}
public override double CalculateArea()
{
return Math.PI * Radius * Radius;
}
}
可以看到 Rectangle 和 Circle 子類別都覆寫了 CalculateArea 方法,但這是選擇性的,因為 Shape 父類別提供了虛擬方法的默認實作。
虛擬方法允許子類別選擇性地覆寫基底方法,並修改或擴展其行為,而不需要強制性提供實作。這使得程式碼更靈活,並允許在需要時進行方法的自訂。
換方式說明他們之間的差異。
抽象方法:
抽象方法就像是一個留給小朋友的謎題,我們告訴小朋友「我們需要一個特別的答案,但我們不會告訴你是什麼答案,你必須自己找出答案。」這樣,小朋友必須去思考和猜測答案,每個人都可以給出自己的答案,但都必須遵循一些規則。
虛擬方法:
虛擬方法就像是一個故事的一部分,我們有一個主要故事,但有些小部分可以有不同的結局。這些小結局是虛擬的,也就是說,你可以自己決定要不要改變它們。如果你喜歡原來的故事,你可以不改變它們,但如果你想要一個不同的結局,你可以創造自己的結局。
總之,抽象方法就像是一個謎題,需要自己找出答案,而虛擬方法就像是一個故事中的選擇,你可以決定是否改變它。這兩種方法都讓程式更有彈性,可以根據需要自行調整。
挑戰第14天完成~