---
# System prepended metadata

title: 【Lua 筆記】物件導向程式設計（Object Oriented Programming，OOP） - part 11
tags: [Lua, 程式語言]

---

【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)