CodeGym /課程 /C# SELF /抽象化與簡化複雜系統

抽象化與簡化複雜系統

C# SELF
等級 22 , 課堂 4
開放

1. 複雜系統的簡單關鍵

複雜的程式系統就像一個大城市:成千上萬的居民、道路、規則、連結。如果你想直接管理每個物件,很快就會搞混,把城市(還有你自己!)的生活變成惡夢。抽象化就像城市的總體規劃:你不用手動盯著每台計程車,但你很清楚交通工具有路線、司機和乘客。

為什麼複雜系統需要抽象化

沒有抽象化的程式碼就像「麵條」一樣——一堆細節,全部東西都直接連在一起,任何小細節都會搞壞其他東西。抽象化把實作細節和整體介面分開,讓你可以「從上面」操作系統,不用每次都鑽進細節裡。

想像一下銀行系統:你想從一張卡轉錢到另一張卡,但你不用知道銀行伺服器怎麼互動或資料庫怎麼設計。對你來說有個簡單的介面:「從一個帳戶轉金額到另一個帳戶」——這就是抽象化的層級。

有時候抽象化就是為了照顧未來的自己:透過明確的抽象,程式碼更好維護、擴充、也更容易解釋。

2. 日常例子

咖啡機的生活例子

你煮咖啡時不用搞懂幫浦、溫度感測器和閥門的結構。只要按個按鈕——就有結果。在程式設計裡,抽象化也是這個功能:把實作細節藏在簡單的介面後面。


// 抽象介面「咖啡機」
public abstract class CoffeeMachine
{
    public abstract void MakeEspresso();
    public abstract void MakeCappuccino();
}

// 具體咖啡機型號的實作
public class FancyCoffeeMachine : CoffeeMachine
{
    public override void MakeEspresso()
    {
        // 製作濃縮咖啡的具體步驟
        Console.WriteLine("研磨、壓實、煮濃縮咖啡...");
    }
    
    public override void MakeCappuccino()
    {
        // 製作卡布奇諾的具體步驟
        Console.WriteLine("研磨、煮咖啡、打奶泡做卡布奇諾...");
    }
}

你是透過CoffeeMachine這個抽象物件互動,煮咖啡的細節都藏在裡面。

3. 實際專案裡的抽象化

開發線上商店

假設我們要開發一個線上商店。有很多實體:商品、購物車、用戶、訂單、付款和配送。沒有抽象化很容易寫出一坨難以擴充的巨石程式碼。

線上商店裡的抽象例子

  1. 商品(Product:
    不管你賣書、冰箱還是電子禮券,所有商品都可以用一個抽象類別Product來表示。
  2. 付款(Payment:
    買家可以用信用卡、PayPal、加密貨幣付款——細節不重要,有個抽象「執行付款」就好。
  3. 配送(Delivery:
    有快遞、郵寄、自取。這些都實作同一個抽象類別「配送」,系統只跟這個共通型別互動。

程式碼範例:配送方式的抽象


public abstract class Delivery
{
    public string Address { get; set; }

    public abstract void Deliver();
}

public class CourierDelivery : Delivery
{
    public override void Deliver()
    {
        Console.WriteLine($"快遞配送到地址: {Address}");
    }
}

public class PickupDelivery : Delivery
{
    public override void Deliver()
    {
        Console.WriteLine($"自取點取貨,地址: {Address}");
    }
}

當訂單成立,倉庫不用管商品怎麼送——只要呼叫order.Delivery.Deliver(),不用看實作細節。這樣很有彈性:要加新配送方式也不用改其他程式碼。

4. 用例子說明抽象化

我們的課程是圍繞一個小應用來設計——比如「農場動物管理」。前幾堂課我們做了AnimalCowDogCat等類別的繼承結構。現在來用抽象化管理農場任務。

抽象化讓動物指令更簡單

假設你要實作「農場流程」:每天所有動物都要餵食並執行自己的動作(比如產奶或吠叫)。我們不想為每種動物寫一堆不同的程序。


public abstract class Animal
{
    public string Name { get; set; }
    public abstract void Feed();
    public abstract void MakeSound();
}

public class Cow : Animal
{
    public override void Feed()
    {
        Console.WriteLine($"{Name}: 吃草。");
    }

    public override void MakeSound()
    {
        Console.WriteLine($"{Name}: 哞~!");
    }
}

public class Dog : Animal
{
    public override void Feed()
    {
        Console.WriteLine($"{Name}: 啃骨頭。");
    }

    public override void MakeSound()
    {
        Console.WriteLine($"{Name}: 汪汪!");
    }
}

這樣有什麼好處?現在你可以用同一種方式處理所有動物,不用管牠是誰:


List<Animal> farmAnimals = new List<Animal>
{
    new Cow { Name = "布蓮卡" },
    new Dog { Name = "沙力克" }
};

foreach (Animal animal in farmAnimals)
{
    animal.Feed();
    animal.MakeSound();
}

想加鵝、羊甚至羊駝——你的迴圈都不用改!

5. 抽象化怎麼幫你降低耦合度

耦合度coupling)就是你程式裡不同部分彼此依賴的程度。高耦合就像學校餐廳:熱水壺壞了,大家都泡不了茶,就算煮麵根本不需要熱水壺。抽象化降低耦合:你只跟介面或抽象類別互動,不用知道「底下」是哪個實作。

視覺圖:抽象層級與依賴關係


+--------------------+     +------------------------+
|   高階程式碼       | --> |      抽象化            |
|      (例如Order)   |     |  (抽象類別 / 介面)     |
+--------------------+     +------------------------+
                                     /      \
                                    /        \
                   +------------------+    +-----------------+
                   | 實作1            |    | 實作2           |
                   | (CourierDelivery)|    | (PickupDelivery)|
                   +------------------+    +-----------------+
高階程式碼只跟抽象互動,不碰具體實作

再看一次抽象化的好處

  • 彈性:可以很快加新型別、改行為,不用動其他程式碼。
  • 可擴展性:系統很容易擴充。我們的線上商店只要加個新配送子類別就能支援新方式。
  • 好測試:抽象化讓系統很適合寫單元測試(可以「替換」實作)。
  • 「開放/封閉原則」(Open/Closed Principle, OCP):程式碼對擴充開放(可以加新實作),對修改封閉(不用改現有程式碼)。

6. 沒有抽象化的問題

沒有抽象化,程式碼很快就變成一堆型別判斷、重複和條件式的麵條。像這樣千萬別學


// 反面教材:完全沒抽象化,只有痛苦
if (animal is Cow)
{
    ((Cow)animal).Feed();
}
else if (animal is Dog)
{
    ((Dog)animal).Feed();
}
else if (animal is Cat)
{
    ((Cat)animal).Feed();
}
// 以此類推...

這種程式碼很難維護:你加一隻羊,就要到處加條件。如果動物還會跳舞,還得複製一大堆區塊到整個專案。

7. 用抽象化設計常見錯誤

錯誤一:過度使用繼承。
新手常常想做很複雜的類別繼承結構,其實有時候用組合更簡單也更可靠。不是所有「擁有」某東西的都要繼承。有時候直接把物件包進去比繼承行為還好。

錯誤二:抽象類別根本沒抽象。
有時候抽象類別裡放了一堆子類根本沒用到的屬性和方法。這違反單一職責原則,也讓程式碼難維護。抽象類別應該定義核心行為,不要變成亂七八糟的方法倉庫。

錯誤三:有重複邏輯卻沒抽象化。
如果好幾個類別有重複邏輯,這可能就是該抽出共通抽象父類的訊號。這種錯誤常常不是因為不懂,而是趕工或規劃不良。

2
任務
C# SELF, 等級 22, 課堂 4
上鎖
動物的抽象範例
動物的抽象範例
2
任務
C# SELF, 等級 22, 課堂 4
上鎖
抽象化與商品配送
抽象化與商品配送
1
問卷/小測驗
抽象類別,等級 22,課堂 4
未開放
抽象類別
抽象化跟抽象類別
留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION