首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

Lua 面向對象編程完全指南:原型繼承、元表機制、單/多重繼承與最佳實踐模式

     編輯:關於C語言

Lua 無內置類,**通過表 + 元表 模擬原型繼承**實現 OOP。本文從核心機制 (元表、__index、self) 入手,逐步構建:類工廠、實例化、繼承鏈、多態調用、多重繼承、私有成員模式,給出生產級可復用代碼框架。

核心認知:表即對象,元表驅動繼承

  • Lua 表是**引用類型**:a = {}; b = a 共享同一張表;a == b 比較引用地址。
  • 方法調用語法糖:obj:method(args) ⇔ obj.method(obj, args),隱式傳遞 self。
  • 元表 __index:查找鍵失敗時,若元表有 __index 表/函數,則轉向該表/函數繼續查找 → **繼承鏈核心**。

最小類系統實現

-- class.lua: 通用類構建器
local function class(base)
    local c = {}             -- 新類表 (原型)
    c.__index = c            -- 關鍵:自身作為 __index
    setmetatable(c, base)    -- 繼承基類 (若 base 為 nil 則無父類)
    -- 構造函數
    function c:new(o)
        o = o or {}
        setmetatable(o, c)   -- 實例元表指向類
        return o
    end
    return c
end
return class

使用示例:定義類、實例化、繼承、多態

local class = require "class"

-- 基類 Account
local Account = class()
Account.balance = 0          -- 類屬性 (默認值)
function Account:deposit(v)  -- 方法 (冒號語法隱式 self)
    self.balance = self.balance + v
end
function Account:withdraw(v)
    if self.balance >= v then
        self.balance = self.balance - v
        return true
    end
    return false
end
function Account:print()
    print("Balance:", self.balance)
end

-- 派生類 SavingsAccount (繼承)
local SavingsAccount = class(Account)
SavingsAccount.interest_rate = 0.03
function SavingsAccount:add_interest()
    self:deposit(self.balance * self.interest_rate)  -- 多態調用基類方法
end
-- 重寫方法
function SavingsAccount:print()
    print("Savings Balance:", self.balance, "Rate:", self.interest_rate)
end

-- 實例化與測試
local acc = Account:new{balance = 100}
acc:deposit(50)
acc:print()  -- Balance: 150

local sav = SavingsAccount:new{balance = 1000}
sav:add_interest()
sav:print()  -- Savings Balance: 1030  Rate: 0.03
sav:withdraw(200)
sav:print()  -- Savings Balance: 830  Rate: 0.03

繼承機制深度解析

-- 類創建時
setmetatable(Child, Parent)  -- Child 的元表 = Parent
Child.__index = Child        -- Child 自己的 __index = Child

-- 實例創建時
setmetatable(instance, Child)  -- instance 元表 = Child

-- 查找鍵 k 流程
instance.k
→ instance 中找不到
→ 元表 Child 中找 k (因 Child.__index = Child)
→ Child 中找不到
→ Child 元表 Parent 中找 k (因 Parent.__index = Parent)
→ ... 直到頂層基類或 nil

**關鍵點**:類表同時扮演「原型」(存方法/默認屬性) 和「元表」(提供 __index) 角色;實例只存**實例特有數據**,方法/默認值全在類鏈上。

多重繼承 (Mixin 模式)

Lua 單繼承鏈,但可通過 **Mixin 表** 組合行為:

local class = require "class"

-- Mixin: 可序列化
local Serializable = {}
function Serializable:serialize()
    local t = {}
    for k, v in pairs(self) do
        if type(v) ~= "function" then t[k] = v end
    end
    return t
end

-- Mixin: 可比較
local Comparable = {}
function Comparable:eq(other)
    return self.id == other.id
end

-- 組合類
local User = class()
function User:new(o)
    o = o or {}
    -- 手動混入 Mixin 方法 (淺拷貝)
    for k, v in pairs(Serializable) do User[k] = v end
    for k, v in pairs(Comparable) do User[k] = v end
    setmetatable(o, User)
    return o
end

local u = User:new{id=1, name="Alice", pwd="secret"}
print(u:serialize())  -- {id=1, name="Alice"} (pwd 非函數但屬敏感,實際需過濾)
print(u:eq({id=1}))   -- true

**生產級多重繼承**推薦用 middleclass、30log 等成熟庫,自動處理鑽石繼承、方法解析順序 (MRO)。

封裝與私有成員模式

1. 閉包工廠模式 (真私有,每實例獨有副本)

function newCounter()
    local count = 0  -- 真私有,外部不可訪問
    return {
        inc = function() count = count + 1; return count end,
        get = function() return count end
    }
end
local c = newCounter()
print(c.inc())  -- 1
print(c.get())  -- 1
-- c.count 不存在

2. 單例/弱表私有表 (共享方法、私有數據分離)

local Private = setmetatable({}, {__mode = "k"})  -- 弱鍵表,不阻礙 GC
local MyClass = class()
function MyClass:new(o)
    o = o or {}
    Private[o] = {secret = 42}  -- 私有數據綁定實例
    setmetatable(o, MyClass)
    return o
end
function MyClass:reveal()
    return Private[self].secret
end

最佳實踐總結

場景推薦模式
簡單單繼承、輕量項目自製 class() 工廠 (本文核心代碼)
複雜繼承、MRO、大項目middleclass / 30log / classic 成熟庫
高性能、大量實例元表復用 + __index 鏈,**避免閉包工廠** (每實例複製方法)
真私有、安全敏感閉包工廠 或 弱表私有數據
組合優於繼承Mixin 表 + 手動混入 或 組合對象持有

常見坑點

  • **忘記 `setmetatable(instance, Class)`** → 實例無法訪問類方法。
  • **類屬性被實例修改污染**:instance.attr = val 會在實例創建新鍵,**不影響類默認值**;但若為表類型且未深拷貝,修改內容會影響所有實例。
  • **遞歸 `__index` 導致死循環**:元表鏈必須最終指向 nil 或無 __index 的表。
  • **垃圾回收**:弱表 (__mode="k/v") 防止私有數據表阻礙實例 GC。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved