[.NET] OOP 三本柱(封裝、繼承、多型)


[.NET] OOP 三本柱(封裝、繼承、多型)

物件導向設計(Object-Oriented Programming, OOP),三本柱分別是 封裝(Encapsulation)、繼承(Inheritance)、多型(Polymorphism) ,除了三大特性外還有一個很重要的特性哪就是 抽象(Abstraction) 。就如同初代寶可夢一樣有 小火龍、妙花種子、傑尼龜 這三本柱,當然還有一位至今還非常重要的 皮卡丘 一樣,這幾個特性之間的關係都是密不可分,所以文章會針對三大特性跟抽象來進行介紹:

封裝 (Encapsulation)

一種將抽象性函式介面的實作細節部份包裝、隱藏起來的方法。同時,它也是一種防止外界呼叫端,去存取物件內部實作細節的手段,這個手段是由程式語言本身來提供的。封裝被視為是物件導向的四項原則之一。
wiki 封裝

封裝可以想像有個黑盒子,不需要理解盒子裡面實作什麼,也不可以控制盒子裡面的東西(Priavate),除非盒子有對外開口(Public),這樣可以確保黑盒子裡面的一致性。就如傑尼龜一樣不需要理解龜殼裡面在做什麼,反正可以讓你固定使用水槍攻擊。

var pokemon = new Squirtle();
Console.WriteLine(pokemon.GetAttack());
// 傑尼龜 使用 水槍 攻擊

public interface IPokemon
{
    public string GetAttack();
}

public class Squirtle : IPokemon
{
    protected readonly string Name = "傑尼龜";

    private readonly string SkillName = "水槍";

    public string GetAttack()
    {
        return $"{Name} 使用 {SkillName} 攻擊";
    }
}

繼承 (Inheritance)

繼承可以使得子類具有父類別別的各種屬性和方法,而不需要再次編寫相同的代碼。在令子類別繼承父類別別的同時,可以重新定義某些屬性,並重寫某些方法,即覆蓋父類別別的原有屬性和方法,使其獲得與父類別別不同的功能。另外,為子類追加新的屬性和方法也是常見的做法。
wiki 繼承

繼承就是保留原本物件功能並可以額外增加功能,類似手機貼了保護貼除了獲得有保護力能力外,還保留手機原本的功能,可以提升程式碼的復用性。用寶可夢來舉例就如妙蛙種子進化成妙蛙草時想保留原本的”藤鞭”技能,妙蛙草就可以繼承妙蛙種子並新增新技能,這樣妙蛙草除了可以使用原本”藤鞭”外又可以使用”飛葉快刀”了,但使用繼承會增加耦合度,所以使用上還需要思考實際情境是否適合。

var pokemon1 = new Bulbasaur();
Console.WriteLine(pokemon1.GetAttack());
// 妙蛙種子 使用 藤鞭 攻擊

var pokemon2 = new Ivysaur();
Console.WriteLine(pokemon2.GetAttack());
Console.WriteLine(pokemon2.GetAttack1());
// 妙蛙草 使用 藤鞭 攻擊
// 妙蛙草 使用 飛葉快刀 攻擊

public interface IPokemon
{
    public string GetAttack();
}

public class Bulbasaur : IPokemon
{
    protected string Name = "妙蛙種子";

    private readonly string SkillName = "藤鞭";

    public string GetAttack()
    {
        return $"{Name} 使用 {SkillName} 攻擊";
    }
}

public class Ivysaur : Bulbasaur
{
    private readonly string SkillName1 = "飛葉快刀";

    public Ivysaur()
    {
        base.Name = "妙蛙草";
    }

    public string GetAttack1()
    {
        return $"{Name} 使用 {SkillName1} 攻擊";
    }
}

多型 (Polymorphism)

指為不同資料類型的實體提供統一的介面,或使用一個單一的符號來表示多個不同的類型。
wiki 多型

用實際案例就是工廠有同一模板,但可以輸出不同實體例如果凍就可以有草莓、葡萄、蘋果…等多種口味,這樣可以提升程式的可擴充性和可維護性。多型可以分為四種下面會一一介紹:

廣義多型 (universal polymorphism)

繼承多型 (inclusion)

繼承多型可以直接使用父類別,可以不理會子類別是什麼類型可以直接使用方法,如果需要使用子類別時方法時需要額外轉型,範例如下:

IPokemon pokemon;

pokemon = new Charizard();
Console.WriteLine(pokemon.GetAttack());
// 噴火龍 使用 噴射火焰 攻擊

pokemon = new CharizardX();
Console.WriteLine(pokemon.GetAttack());
// 超級噴火龍X 使用 噴射火焰 攻擊
Console.WriteLine(((CharizardX)pokemon).GetAttack1());
// 超級噴火龍X 使用 龍爪 攻擊

public class Charizard : IPokemon
{
    protected string Name = "噴火龍";

    private readonly string SkillName = "噴射火焰";

    public string GetAttack()
    {
        return $"{Name} 使用 {SkillName} 攻擊";
    }
}

public class CharizardX : Charizard
{
    public CharizardX()
    {
        base.Name = "超級噴火龍X";
    }

    private readonly string SkillName1 = "龍爪";

    public string GetAttack1()
    {
        return $"{Name} 使用 {SkillName1} 攻擊";
    }
}
參數多型 (parametric)

List<T> 中的 T 就是參數型別,依據參數的型別決定實作的內容,範例如下:

public interface IPokemon
{
    public string GetAttack();
}

var pokemons = new List<IPokemon>();

特設多型 (ad hoc polymorphism)

多載 (overloading)

相同方法但參數不同,不論是參數數量或形態都屬於多載,範例如下:

public class Pokemon
{
    public string GetName()
    {
        return "喵喵";
    }

    public string GetName(string name)
    {
        return name;
    }
}
強制同型 (coercions)

自動將型別轉換,下面範例就將金額自動轉換成 string,範例如下:

var pokemon1 = new Bulbasaur();
Console.WriteLine(pokemon1.GetAttack());
// 喵喵 使用 聚寶功 攻擊,獲得 100 元

public interface IPokemon
{
    public string GetAttack();
}

public class Bulbasaur : IPokemon
{
    protected string Name = "喵喵";

    private readonly string SkillName = "聚寶功";

    private readonly decimal Money = 100m;

    public string GetAttack()
    {
        return $"{Name} 使用 {SkillName} 攻擊,獲得 {Money} 元";
    }
}

抽象 (Abstraction)

是指以縮減一個概念或是一個現象的資訊含量來將其廣義化(Generalization)的過程,主要是為了只保存和一特定目的有關的資訊。例如,將一個皮製的足球抽象化成一個球,只保留一般球的屬性和行為等資訊。相似地,亦可以將快樂抽象化成一種情緒,以減少其在情緒中所含的資訊量。
wiki 抽象化

簡單地說把真實情況轉換成類別,而這個類別可以包含 狀態(屬性) 或是 行為(方法)。例如寶可夢我們如果只關心寶可夢名字跟 屬性使用技能 就可以將其抽象化為下面範例:

var pokemon = new Pokemon();
pokemon.Name = "皮卡丘";
pokemon.Property = "電";
pokemon.SkillName = "十萬伏特";

Console.WriteLine(pokemon.GetProperty());
// 皮卡丘 屬性: 電
Console.WriteLine(pokemon.GetAttack());
// 皮卡丘 使用 十萬伏特 攻擊 

public class Pokemon
{
    public string Name { get; set; }

    public string Property { get; set; }

    public string SkillName { get; set; }

    public string GetProperty()
    {
        return $"{Name} 屬性: {Property}";
    }

    public string GetAttack()
    {
        return $"{Name} 使用 {SkillName} 攻擊";
    }
}

參考資料

小小菜鳥的成長日記 - OOP 三大特性

伍夜黃昏之時 - 三大特性:封裝、繼承、多型

OOP 物件導向的四個特性

設計模式前置知識

存取範圍層級


作者: PuTaoNi
版權聲明: 本站所有文章除特別聲明外,均採用 CC BY 4.0 許可協議。轉載請註明來源 PuTaoNi !
 上一篇
[.NET] 什麼是泛型 (Generics) [.NET] 什麼是泛型 (Generics)
泛型是在 C# 2.0 才被加入的新功能,主要是將類別參數化`T`,讓設計類別(Class)、結構(Struct)、介面(Interface)與方法(Method)時可以使用一個或多個參數,這樣就可以增加重用性(Reusability)、類型安全(Type safety)與效率(Efficiency),下面的例子就是簡單的泛型類別。
2022-03-22
下一篇 
[.NET] 併發基本三原則原子性、可見性、有序性 [.NET] 併發基本三原則原子性、可見性、有序性
在設計併發(Concurrency)程式時往往忽略基本三原則原子性(Atomic)、可見性(Visibility)、有序性(Ordering),可能在程式執行時造成非預期的錯誤,透過下面介紹來瞭解這些原則因而避免錯誤產生。
2021-12-21
  目錄