物件導向中的多型、繼承概念與應用

閱讀時間約 7 分鐘
物件導向(Object-Oriented Programming,OOP) 可以用來提高程式碼的可讀性、可維護性和可擴展性,同時還能夠促進程式碼的重用和組織。

多型 (Polymorphism)

以挖礦為例,當挖掘鑽石礦時,會獲得鑽石等物品。我們可以定義一個統一的礦物基類,並通過覆蓋其方法來確保挖掘特定礦物時會掉落相應的物品。這比傳統的 if-else 判斷方式更為簡潔,只需判斷挖掘的對象是否為礦物,無需關注具體的礦物類型。這樣的多型實現使程式碼更具靈活性和可擴展性。

多型與指標密切相關。通過改變指標來決定當前的礦物類型,指向不同的具體礦物類別(如鑽石礦或金礦),從而實現多型。利用 virtual 和 override 關鍵字,我們可以動態地選擇合適的函數實現。

純虛擬函數 (Pure Virtual Function)

在基類中聲明純虛擬函數,要求所有子類必須提供具體實現。這樣可以確保每個子類都有自己專屬的行為。

物件轉型 (Object Casting)

將衍生類別轉型為基類別是多型的一部分。我們可以利用基類別的指標操作不同類型的物件,實現通用處理。例如,一個人挖礦時,無論挖掘的是何種礦物,都會掉落相應的物品。這種方法可以省去大量的 if-else 判斷邏輯。

為什麼需要物件轉型?

物件轉型可以方便地替換為我們「想要的類型」,提高程式碼的靈活性和可重用性。

物件轉型的問題

如果將子類別轉型為基類別,而基類別中沒有相關的定義,則無法正常運行。

虛擬函數與覆蓋 (Virtual and Override)

如果基類中定義了 virtual 函數,而子類中對其進行了覆蓋(override),編譯器會在運行時自動選擇合適的子類實現。

不加 virtual 和 override 的情況

如果不使用 virtual 和 override,嘗試通過基類指標調用子類中的函數時,會出現調用基類函數的情況,導致返回不正確的結果。


提早綁定與延遲綁定 (Early Binding and Late Binding)

提早綁定

在編譯時期進行綁定,將所有函數綁定在一起。

延遲綁定

在運行時期進行綁定,允許程式在執行過程中動態綁定需要的函數定義。

沒有 virtual 的情況

使用提早綁定,直接在編譯時期綁定基類的定義。

加了 virtual 的情況

編譯器會使用延遲綁定,在運行時期動態尋找函數的定義,從而調用子類的相關函數實現。


虛擬解構函數 (Virtual Destructor)

對於具有衍生類別的物件,解構順序是從最底層子類的解構子開始,逐步釋放到最上層基類的解構子。這樣可以確保所有資源都能正確釋放,避免內存泄漏。

包含 (Inclusion)

以熔爐為例,減少同樣函數原型、不同類別、不同實作細節所造成的特殊判斷情況。例如,不同的礦物可以逐一燒成錠,這樣可以減少冗長的判斷邏輯。

問題

如果有多種類型的礦物,需要為每一種礦物編寫對應的處理邏輯。

解決方法

利用繼承和多型,只要繼承自礦物基類,並實現相應的燒製方法,就能大大減少重複的實現細節。


函數匹配與函數重載 (Function Matching and Function Overloading)

函數匹配

針對同名但參數不同的函數,C++ 會自動選擇對應的函數實現。例如,AddOre() 函數可以有不同的參數個數和類型,C++ 會自動匹配合適的函數。

函數重載

相同名稱但參數不同的函數。函數重載與函數匹配實現了相同的功能,使得同一個操作可以對多種類型的數據進行處理。

運算符重載 (Operator Overloading)

可以重載 C++ 內建的運算符,使得物件之間可以進行相加、相減等運算。例如,玩家的移動可以通過位置(三維空間的座標 x, y, z)的相加來實現。


類別模板 (Class Template)

使用模板讓類別使用通用型別,並將類別中通用型別變成指定型別。這樣可以避免為每種礦物創建不同的類別,而是使用模板來實現通用化。

模板綁定

通過提早綁定,在編譯時期自動綁定傳入型別的函數。


函數模板 (Function Template)

對特定函數使用通用型別 (Generic),使得函數可以處理不同型別的數據。

使用時機

根據 StackOverflow 上的專家回答,以下情況應該使用類別模板:

  • 如果某個成員的型態是 T。
  • 如果某個虛擬函數需要型別是 T。

鴨子類型 (Duck Typing)

不需要關心物件是否真的是礦物,只要能燒就可以處理。例如,木頭可以像礦物一樣燒,只要滿足最低需求即可運行。這種方式讓程式碼更具彈性。

最小需求

只要編譯能通過,無論是否符合語義,只要符合規則即可運行。


多型結論

通過抽象具體型別,變成通用處理,解決程式碼冗長的問題。

包含 (Inclusion)

減少同樣函數原型、不同類別、不同實作細節所造成的特殊判斷情況。

函數重載 (Function Overload)

相同名稱但不同參數的函數。

模板 (Template)

將不同型別的部分抽象成模板,根據使用者傳入的型別進行替換。


組合與全釋放 (Composition and Aggregation)

成員內具有其他類別成員,這個類別擁有「類別成員」的所有權。建構子或 Setter 中設定這些成員。例如,不同的食材組合可以創造不同的炒飯。當物件被解構時,所有成員都會被釋放。

組合的優點

  1. 維護方便。
  2. 新增道具簡單直觀。
  3. 擴展性好,只需新增類別即可。

組合的解構

成員內的所有成員都會被釋放。由於物件擁有其成員,當物件被解構時,其成員也應一同被釋放。


物件委派與聚合 (Delegation and Aggregation)

物件委派

組合可以實現類似「委派」的功能。例如,將裝備丟到附魔台中進行附魔。

物件的聚合

組合內的成員不會全部釋放。玩家有附魔台,裝備丟進去附魔,但附魔台消失時,裝備不會消失。委派是附魔的動作,組合是將附魔台和裝備結合。

介面

通過純虛擬函數和繼承實現「介面」,讓所有會攻擊玩家的對象都繼承一個介面,並讓子類實現細節。


耦合 (Coupling)

描述改變某些東西時造成的影響程度。

高耦合度

程式碼重複度低,但繼承鏈很長。

低耦合度

程式碼會重複,但繼承鏈短,維護方便。


組合與介面解決繼承問題

以怪物繼承為例,將重複的程式碼提到父類以提升程式碼整潔度,但容易增加耦合度。

傳統的類別繼承

將重複的程式碼提到父類以提升程式碼整潔度,但容易增加耦合度,導致維護困難。

新的繼承方式

不繼承 Mob,而是將 Mob 當成類別成員。初始化這些類別後,依然保有 Mob 的特性,通過組合與介面取代繼承類別。


The Diamond Problem

地獄幽靈和終界龍都繼承 Mob 的成員和函數,可能導致編譯時期的重複。可以利用組合與介面解決這個問題。

依賴注入 (Dependency Injection)

在建構子中將類別作為參數,根據傳入的物件做出對應行為。例如,熔爐丟肉燒會飄香,丟礦石燒得較慢。

結論

組合與介面解決繼承問題,扁平化繼承鏈,降低耦合度,更易於維護。

10會員
3內容數
歡迎來到我的部落格!這裡是個佛系經營的角落,我會在這裡分享我的心路歷程,及不定時生活大小事分享,包括科技、閱讀、星座、金融、好物等。 希望分享的內容能夠幫助大家,與大家一同學習成長,無論是知識的豐富還是心靈的提升。歡迎留言、討論,分享你的看法和經驗。如文章內容有誤,請不吝指教,我會虛心接受並改進! 什麼都發~
留言0
查看全部
發表第一個留言支持創作者!
你可能也想看
物件導向的SOLID說到物件導向就必須提五個原則,統稱SOLID,它被認為是物件導向的重要概念。這五個原則並不只適用於物件導向,事實上它很像函數式編程的習慣。它的命名很奇怪且容易讓人混淆,所以我會用我自己的翻譯解釋。 Single Responsibility Principle 是「單一職責原則」,認為一個模組
avatar
have bear
2024-02-02
物件導向不好用讓我在這篇文章總結一下前面對物件導向設計的討論,我們討論了物件導向的四個特性:繼承、抽象、多型、封裝,分析了它們的問題,並跟函數式編程的思維做比較。我們引入了與之相對應的特性:泛型、特性系統、模組化,有些特性雖然跟那四個特性很像,但在一些細微的地方有不同的詮釋,使得整體思考方式很不一樣。 「繼
avatar
have bear
2024-01-30
物件導向是什麼在現代,物件導向雖然仍是主流,函數式慢慢得到關注。物件導向並不適合所有的程式邏輯,但在像是Java的物件導向的程式語言中,幾乎所有值都被當作物件,因此在一些情境下Java寫起來會非常冗余。物件導向流行的原因大概是因為它的思考方式比較符合我們對於世界的認知,但邏輯推理與解決問題的方式卻不一定符合我們的
avatar
have bear
2023-12-06
【中低階級】-[物件導向]-介面(Interface)、抽象(abstract)與虛擬(virtual)的宣告  物件導向說到這邊其實修飾詞不只有之前所提的public、private這些,還有可能遇到抽象(abstract)、虛擬(virtual)、覆寫(override)、隱藏(new)、密封(sealed)、介面(interface)等等,我們就來看一下它們的差異吧~
Thumbnail
avatar
橙果杏
2023-04-24
【中低階級】-[物件導向]-三大特性-多型三、多型(polymorphism)   多型就是在同事物下使用不同條件就可以表現出不同的形態。舉例來說:以「小貓」為例它會有不同的屬性,包括品種、名字、年齡等等,對於每一隻就會有不同的動作如:趴、走、坐、跑等等行為。那麼 .小貓就屬於「同一事物」 .品種、名字、年齡這些就屬於「不同條件」 .趴、走
Thumbnail
avatar
橙果杏
2023-04-20
【中低階級】-[物件導向]-三大特性-繼承二、繼承(inheritance) 繼承就是假如A(子)類別去繼承B(父)類別,那麼A(子)類別可以直接去使用B(父)類別非私有的屬性和方法,但是A(子)只能繼承一個B(父)類別ㄛ! 一樣的道理可以比喻為:爸爸跟小孩之間的關係。小孩可以去運用爸爸的資源,但是爸爸的工作屬於他自己的不能跟小孩一起分享,
Thumbnail
avatar
橙果杏
2023-04-19
【中低階級】-[物件導向]-三大特性-封裝,先來分享一下封裝是怎麼一回事。 一、封裝(Encapsulation) 封裝就是把一些功能的處理程序或是資料包起來,也對於程式碼做權限的設定做一層保護的機制,這是為了防止程式碼被竄改,所以有了封裝可以保障我們資料的隱密性,甚至封裝也是一種將一些處理程序隱藏起來,讓使用者使用時可以更加單純。 1.什
Thumbnail
avatar
橙果杏
2023-04-15
【中低階級】-[物件導向]-介紹  物件導向(Object Oriented Programming,又稱OOP),在程式領域中很常會看到一些英文代稱,如果不懂的話就比較會不知道在說些什麼,所以除了知道中文名稱外也要稍微知道一下它的代稱是什麼會比較好唷! 一、什麼是物件導向?   把程式寫出來的功能集合成一個類別(class),接
Thumbnail
avatar
橙果杏
2023-04-13
物件導向設計原則:SOLID 單一職責原則(Single Responsibility Principle) 里氏替換原則(Liskov Substitution Principle) 依賴反轉原則(Dependence Inversion Principle) 最少知識原則(得墨忒耳定律)(Law Of Demeter)
Thumbnail
avatar
一代軍師
2022-08-15
物件導向設計原則 - SOLID本筆記除了以文字說明SOLID設計原則以外,並以Java code實際舉例。 Single Responsibility Principle (SRP) 單一職責原則 每個人負責屬於自己的職責,不該承擔太多職責,大家各自做自己應該做的事情,且不會互相干擾。 續上程式碼,修改如下: Output:
Thumbnail
avatar
Vic Lin
2022-06-26