# [.NET] OOP 三本柱(封裝、繼承、多型) ###### tags: `Bolger` `.NET` ## [.NET] OOP 三本柱(封裝、繼承、多型) 物件導向設計(Object-Oriented Programming, OOP),三本柱分別是 **封裝(Encapsulation)、繼承(Inheritance)、多型(Polymorphism)**,除了此三大特性外還有個不得不提到的重要東西就是**抽象(Abstraction)**。就跟寶可夢有三本柱**小火龍、妙花種子、傑尼龜**,當然還有最重要的**皮卡丘**一樣,此特性之間都是密不可分的關係,所以此文章會針對三大特性跟抽象來進行介紹: ### 封裝 (Encapsulation) > 一種將抽象性函式介面的實作細節部份包裝、隱藏起來的方法。同時,它也是一種防止外界呼叫端,去存取物件內部實作細節的手段,這個手段是由程式語言本身來提供的。封裝被視為是物件導向的四項原則之一。 [wiki 封裝][wiki封裝] 封裝可以想像有個黑盒子,不需要理解盒子裡面實作什麼,也不可以控制盒子裡面的東西`(Priavate)`,除非盒子有對外開口`(Public)`,這樣可以確保黑盒子裡面的**一致性**。就如傑尼龜一樣不需要理解龜殼裡面在做什麼,反正可以讓你固定使用水槍攻擊。 ``` csharp 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 繼承][wiki繼承] 繼承就是保留原本物件功能並可以額外增加功能,類似手機貼了保護貼除了獲得有保護力能力外,還保留手機原本的功能,可以提升程式碼的復用性。用寶可夢來舉例就如妙蛙種子進化成妙蛙草時想保留原本的"藤鞭"技能,妙蛙草就可以繼承妙蛙種子並新增新技能,這樣妙蛙草除了可以使用原本"藤鞭"外又可以使用"飛葉快刀"了,但使用**繼承會增加耦合度**,所以使用上還需要思考實際情境是否適合。 ``` csharp 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 多型][wiki多型] 用實際案例就是工廠有同一模板,但可以輸出不同實體例如果凍就可以有草莓、葡萄、蘋果...等多種口味,這樣可以提升程式的可擴充性和可維護性。多型可以分為四種下面會一一介紹: #### 廣義多型 (universal polymorphism) ##### 繼承多型 (inclusion) 繼承多型可以直接使用父類別,可以不理會子類別是什麼類型可以直接使用方法,如果需要使用子類別時方法時需要額外轉型,範例如下: ``` csharp 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 就是參數型別,依據參數的型別決定實作的內容,範例如下: ``` csharp public interface IPokemon { public string GetAttack(); } var pokemons = new List<IPokemon>(); ``` #### 特設多型 (ad hoc polymorphism) ##### 多載 (overloading) 相同方法但參數不同,不論是參數數量或形態都屬於多載,範例如下: ``` csharp public class Pokemon { public string GetName() { return "喵喵"; } public string GetName(string name) { return name; } } ``` ##### 強制同型 (coercions) 自動將型別轉換,下面範例就將金額自動轉換成 `string`,範例如下: ``` csharp 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 抽象化][wiki抽象化] 簡單地說把真實情況轉換成類別,而這個類別可以包含 **狀態(屬性)** 或是 **行為(方法)**。例如寶可夢我們如果只關心寶可夢名字跟 `屬性` 與 `使用技能` 就可以將其抽象化為下面範例: ``` csharp 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 三大特性](https://dotblogs.com.tw/wuu1992/2017/10/16/213954) [伍夜黃昏之時 - 三大特性:封裝、繼承、多型](https://rileylin91.github.io/2020/06/19/OOP-2-OOP-Feature/) [OOP 物件導向的四個特性](https://coreychen71.github.io/posts/2019-10/oop/) [設計模式前置知識](https://hackmd.io/@CityChen/S18Fve5KY#%E7%B9%BC%E6%89%BF-amp-%E5%B0%81%E8%A3%9D-amp-%E5%A4%9A%E5%9E%8B) [存取範圍層級](https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/accessibility-levels) [wiki封裝]: "https://zh.wikipedia.org/zh-tw/%E5%B0%81%E8%A3%9D_(%E7%89%A9%E4%BB%B6%E5%B0%8E%E5%90%91%E7%A8%8B%E5%BC%8F%E8%A8%AD%E8%A8%88)" [wiki繼承]: "https://zh.wikipedia.org/wiki/%E7%BB%A7%E6%89%BF_(%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6)" [wiki多型]: "https://zh.wikipedia.org/wiki/%E5%A4%9A%E6%80%81_(%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6)" [wiki抽象化]: "https://zh.wikipedia.org/wiki/%E6%8A%BD%E8%B1%A1%E5%8C%96" <!-- 基本圖 --> [C#基本圖]: https://www.mindomo.com/hu/mindmap/alm-for-c-17fd02c289d846dcbc76c02422606b0c
×
Sign in
Email
Password
Forgot password
or
Sign in via Google
Sign in via Facebook
Sign in via X(Twitter)
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
Continue with a different method
New to HackMD?
Sign up
By signing in, you agree to our
terms of service
.