【Lua 筆記】物件導向程式設計(Object Oriented Programming,OOP) - part 11 === 目錄(Table of Contents): [TOC] --- 由於有款遊戲叫做 CSO(Counter-Strike Online),內建模式創世者模式(Studio)新增使用 Lua 及其遊戲的 API,所以突發奇想製作這個筆記。 這個筆記會在一開始先著重純粹的程式設計自學,在最後的章節才會與 CSO 遊戲 API 進行應用。 小提醒:此為本筆記最後一章,恭喜你可以準備結合CSO的API實作了。 物件導向程式設計(Object Oriented Programming,OOP) --- 物件導向程式設計(Object-Oriented Programming, OOP)是一種程式設計範式,強調使用「物件」來設計軟體和程式碼。物件是具有狀態(屬性)和行為(方法)的實體。OOP 的主要目的是提高程式碼的可重用性、可維護性和可擴展性。 舉個例子:比如我現在正在 coding,用物件導向程式設計的邏輯來看的話,就是如以下表格所示(參考自:[何謂物件導向程式設計 - HackMD](https://hackmd.io/@metal35x/rk2uiTnXI)): | 類別(class) | 物件名稱(name) | 物件屬性(property) | 物件操作方法(method) | | -------- | -------- | -------- | -------- | | 人類 | 我(LukeTseng) | 具有靈活的雙手 | 操作鍵盤滑鼠 | | 電腦 | 個人電腦 PC | CPU:i5-6400、GPU:MSI RTX 4060 Ti 8G 等 | 控制各個元件 | | 鍵盤 | Logic Signature K855 | 具有許多鍵位(如:WASD等) | 鍵盤輸入文字 | | 螢幕 | MSI(我忘記是啥型號了XD) | 100 Hz、內建喇叭等 | 顯示彩色畫面、文字 | 物件,想像成是一個形體或物品,就好比我這個個體就是一種物件,電腦也是一種物件。但是細分下去,CPU、GPU等等也都可以被稱為是一種物件。 回歸到 Lua 本身,其實他並不是完全或原生的物件導向的程式語言,但是我們可以透過 table、metatable 模擬出物件導向的特性。 以下是關於物件導向的特性: 1. **封裝(Encapsulation)**:將物件的狀態(屬性)和行為(方法)封裝在一起,並隱藏物件的內部實際細節。這樣可以保護物件的狀態不被外部直接修改,只能通過物件的方法來運算。 2. **繼承(Inheritance)**:能讓一個類別(子類別)從另一個類別(父類別)繼承屬性和方法。這樣可以重複使用父類別的程式碼,並在子類別中擴展或修改其行為(方法)。 3. **多型(Polymorphism)**:能讓不同的物件以相同的方式響應相同的方法呼叫。表示可以用相同的介面來操作不同型態的物件,且不需要知道它們的具體型態。(簡短說就是指同一行為能有不同結果,例如滑鼠左鍵點某個網站連結直接導向那個網站,在網站裡面點各個按鈕又是不一樣的結果) 4. **抽象(Abstraction)**:指只顯示物件的必要部分,隱藏不必要的細節。可以簡化複雜系統的設計,讓 user 只需要用到重要的部分。 關於上述四點,筆者這邊用 python 的例子來舉例: ```python= # 封裝(Encapsulation) class Person: def __init__(self, name, age): self.__name = name # 私有屬性 -> 封裝特性:只有這個類別裡面的方法才能用這個物件 self.__age = age # 私有屬性 -> 封裝特性:只有這個類別裡面的方法才能用這個物件 def get_name(self): return self.__name def set_age(self, age): if age > 0: self.__age = age ``` ```python= # 繼承(Inheritance) class Animal: def __init__(self, name): self.name = name def speak(self): pass class Dog(Animal): # Dog 類別繼承 Animal 類別,所以 Dog 可以用 Animal 的方法跟物件 def speak(self): return "Woof!" ``` ```python= # 多型(Polymorphism) class Animal: def __init__(self, name): self.name = name def speak(self): pass class Dog(Animal): def speak(self): return "Woof!" class Cat(Animal): def speak(self): return "Meow!" def make_animal_speak(animal): print(animal.speak()) # 同種呼叫方式有不同結果(因為不同動物:dog、cat) dog = Dog("Buddy") cat = Cat("Whiskers") make_animal_speak(dog) # Woof! make_animal_speak(cat) # Meow! ``` 抽象的部分就不舉例了(因為抽象概念其實好了解,另外程式上也用到語法糖的語法,只顯示重要的部份給 user,簡化使用者介面的複雜程度) 接下來讓我們進入 Lua 版本的物件導向程式設計 ### Lua 物件導向 --- 物件是由屬性和方法所組成的。Lua 中最基本的資料結構是 table,所以需要用 table 來描述物件的屬性。 方法其實就是由函數(function)所組成的,所以可以在 lua 當中用 table + function 重現 class。 以下是用表與函數結合的模擬類別(來自:[Lua 面向对象 | 菜鸟教程](https://www.runoob.com/lua/lua-object-oriented.html)): ```lua= Account = {balance = 0} function Account.withdraw (v) Account.balance = Account.balance - v end ``` 這個函數的作用域被保存在 Account 裡面做使用,也就是所謂的方法(Method),可以用以下這方式呼叫這個函數: ``` Account.withdraw(100.00) ``` 是不是就有點像之前學過的字串方法呢?像是:string.upper() 一樣。 以下是筆者自創範例: ```lua= -- 定義 Person 類別 Person = {} -- Person 類別的建構方法 function Person:new(name, age) local newObj = { name = name, age = age } self.__index = self return setmetatable(newObj, self) end -- Person 類別的方法:顯示個人資訊 function Person:showInfo() print("Name: " .. self.name .. ", Age: " .. self.age) end -- 創建 Person 類別的實例 local person1 = Person:new("John", 30) -- 呼叫實例的方法 person1:showInfo() -- 存取 person1 的屬性 print(person1.age) ``` :::info 建構方法(又稱構造函數:Constructor):主要用於在創建物件時「初始化」物件,給物件成員變數(屬性)賦予初始值。 `local person1 = Person:new("John", 30)` 創建一個物件(實例:Instance,一個具體的物件,有詳細的內容,在這範例當中包含人名跟年齡)。實例就直接看成一個個體,像筆者 LukeTseng 是一個個體,更詳細一點內容就是我的身高年齡血壓等等,每個實例的屬性值可以是不同的,表示每個實例都是獨立的,擁有自己的狀態。 `print(person1.age)` 存取 person1 實例的屬性。 ::: :::info **為什麼用冒號 `:`?** ```lua -- 使用點(.)呼叫 person1.showInfo(Person) -- 使用冒號(:)呼叫 person1:showInfo() ``` 用 `.` 需要明確輸入參數進去,用冒號的話,Lua 自動將 Person 作為 self 參數傳遞給showInfo 函數,使呼叫動作更加簡潔、直觀。 ::: ### Lua 繼承 --- 可以透過 metatable 實現。 ```lua= -- 父類別 Animal 定義 Animal = {} Animal.__index = Animal function Animal:new(name) local instance = setmetatable({}, Animal) instance.name = name return instance end function Animal:speak() print(self.name .. " makes a sound.") end -- 子類別 Dog 繼承自 Animal Dog = setmetatable({}, {__index = Animal}) function Dog:new(name) local instance = setmetatable(Animal:new(name), Dog) return instance end function Dog:speak() print(self.name .. " barks.") end local animal = Animal:new("Some animal") animal:speak() -- Some animal makes a sound. local dog = Dog:new("Rex") dog:speak() -- Rex barks. ``` 總結 --- 物件導向程式設計(Object-Oriented Programming, OOP)是一種強調使用「物件」來設計軟體和程式碼的程式設計範式。物件是具有狀態(屬性)和行為(方法)的實體。OOP 的主要目的是提高程式碼的可重用性、可維護性和可擴展性。 OOP 的四大特性包括: * 封裝(Encapsulation):將物件的狀態和行為封裝在一起,隱藏內部細節,只能通過物件的方法來操作狀態。 * 繼承(Inheritance):允許一個類別(子類別)從另一個類別(父類別)繼承屬性和方法,實現程式碼的重用。 * 多型(Polymorphism):不同的物件可以以相同的方式響應相同的方法呼叫,實現同一行為有不同的執行結果。(簡單來說就是同一行為有不同結果) * 抽象(Abstraction):隱藏不必要的細節,只顯示物件的必要部分,簡化系統設計。 Lua 雖然不是原生物件導向的語言,但可以透過 table 和 metatable 來模擬類別(class)、繼承(inheritance)等物件導向的特性。 Lua 用 table 來描述物件的屬性,並通過函數(function)來實現方法。透過 metatable 的機制,Lua 能夠模擬出類別的繼承。 參考資料 --- [何謂物件導向程式設計 - HackMD](https://hackmd.io/@metal35x/rk2uiTnXI) [Programming in Lua : 16](https://www.lua.org/pil/16.html) [[Lua] 程式設計教學:撰寫基於物件的 (object-based) 程式 | 開源技術教學](https://opensourcedoc.com/lua-programming/object/) [Lua 面向对象 | 菜鸟教程](https://www.runoob.com/lua/lua-object-oriented.html)