前置知识: Lua

元表与面向对象编程

00:00
4 min Intermediate

元表机制、元方法与面向对象设计模式。

1. 元表 (Metatables)

1.1 元表基础

元表允许我们改变 Table 的行为,通过定义元方法来重载运算符、控制访问等。

 -
 local meta = {
  -- 重载加法运算符
  __add = function(a, b)
  return a.value + b.value
  end,
  -- 重载乘法运算符
  __mul = function(a, b)
  return a.value * b.value
  end,
  -- 重载字符串转换
  __tostring = function(self)
  return "Value: " .. self.value
  end
 }
 -
 local t1 = { value = 10 }
 local t2 = { value = 20 }
 setmetatable(t1, meta)
 setmetatable(t2, meta)
 -
 print(t1 + t2) -- 30
 print(t1 * t2) -- 200
 print(t1) -- Value: 10

1.2 常用元方法

元方法描述示例
__add加法运算符 +a + b
__sub减法运算符 -a - b
__mul乘法运算符 *a * b
__div除法运算符 /a / b
__mod取模运算符 %a % b
__pow幂运算符 ^a ^ b
__unm一元负号 --a
__concat连接运算符 ..a .. b
__eq等于运算符 ==a == b
__lt小于运算符 <a < b
__le小于等于运算符 <=a <= b
__index访问不存在的键时调用t[key]
__newindex赋值不存在的键时调用t[key] = value
__call调用表作为函数时调用t()
__tostring转换为字符串时调用tostring(t)
__len获取长度时调用#t

1.3 高级元表技巧

1.3.1 保护表

 function readonly(table)
  return setmetatable({}, {
  __index = table,
  __newindex = function(self, key, value)
  error("Attempt to modify read-only table", 2)
  end,
  __metatable = false -- 防止获取元表
  })
 end
 local original = { value = 10 }
 local readonly_table = readonly(original)
 print(readonly_table.value) -- 10
 -

1.3.2 自动创建表

 function autotable()
  return setmetatable({}, {
  __index = function(self, key)
  local value = autotable()
  rawset(self, key, value)
  return value
  end
  })
 end
 local t = autotable()
 t.a.b.c.d = 10
 print(t.a.b.c.d) -- 10

2. 面向对象编程 (OOP)

2.1 基础类实现

 -
 local Person = {}
 Person.__index = Person
 -
 function Person:new(name, age)
  local o = {}
  setmetatable(o, self)
  o.name = name
  o.age = age
  return o
 end
 -
 function Person:greet()
  return "Hello, my name is " .. self.name .. " and I'm " .. self.age .. " years old"
 end
 -
 local alice = Person:new("Alice", 30)
 print(alice:greet()) -- Hello, my name is Alice and I'm 30 years old

2.2 继承

 -
 local Animal = {}
 Animal.__index = Animal
 function Animal:new(name)
  local o = {}
  setmetatable(o, self)
  o.name = name
  return o
 end
 function Animal:speak()
  return "Some generic sound"
 end
 -
 local Dog = {}
 setmetatable(Dog, { __index = Animal })
 dog.__index = Dog
 function Dog:new(name, breed)
  local o = Animal:new(name)
  setmetatable(o, self)
  o.breed = breed
  return o
 end
 function Dog:speak()
  return "Woof!"
 end
 -
 local rover = Dog:new("Rover", "Labrador")
 print(rover:speak()) -- Woof!
 print(rover.name) -- Rover
 print(rover.breed) -- Labrador

2.3 多继承

 function createClass(...)
  local class = {}
  local parents = { ... }
  -- 设置元表,实现多继承
  setmetatable(class, {
  __index = function(self, key)
  for _, parent in ipairs(parents) do
  local value = parent[key]
  if value then
  return value
  end
  end
  end
  })
  class.__index = class
  function class:new(o)
  o = o or {}
  setmetatable(o, self)
  return o
  end
  return class
 end
 -
 local A = {}
 A.__index = A
 function A:methodA() return "Method A" end
 local B = {}
 B.__index = B
 function B:methodB() return "Method B" end
 -
 local C = createClass(A, B)
 -
 local c = C:new()
 print(c:methodA()) -- Method A
 print(c:methodB()) -- Method B

2.4 访问控制

 local Account = {}
 Account.__index = Account
 function Account:new(balance)
  local o = {}
  setmetatable(o, self)
  o._balance = balance or 0 -- 私有变量
  return o
 end
 function Account:deposit(amount)
  self._balance = self._balance + amount
 end
 function Account:withdraw(amount)
  if amount <= self._balance then
  self._balance = self._balance - amount
  return
  else
  return false
  end
 end
 function Account:getBalance()
  return self._balance
 end
 -
 local account = Account:new(1000)
 account:deposit(500)
 print(account:getBalance()) -- 1500
 print(account._balance) -- 1500 (注意:Lua 没有真正的私有变量)

3. 协程 (Coroutines)

3.1 基础使用

 -
 local co = coroutine.create(function(name)
  print("Hello, " .. name)
  local value = coroutine.yield("Yielding...")
  print("Received: " .. value)
  return "Done"
 end)
 -
 local status, result = coroutine.resume(co, "Alice")
 print(status, result) --  Yielding...
 -
 status, result = coroutine.resume(co, "World")
 print(status, result) --  Done
 -
 status, result = coroutine.resume(co)
 print(status, result) -- false cannot resume dead coroutine

3.2 协程状态

 local co = coroutine.create(function()
  print("Starting")
  coroutine.yield()
  print("Resumed")
 end)
 print(coroutine.status(co)) -- suspended
 coroutine.resume(co) -- Starting
 print(coroutine.status(co)) -- suspended
 coroutine.resume(co) -- Resumed
 print(coroutine.status(co)) -- dead

3.3 生产者-消费者模式

 function producer()
  return coroutine.create(function()
  local i = 0
  while  do
  i = i + 1
  coroutine.yield(i)
  end
  end)
 end
 function consumer(prod)
  while  do
  local status, value = coroutine.resume(prod)
  if status then
  print("Received: " .. value)
  if value >= 5 then break end
  else
  break
  end
  end
 end
 local prod = producer()
 consumer(prod)

3.4 协程池

 local function createCoroutinePool(size, func)
  local pool = {}
  for i = 1, size do
  pool[i] = coroutine.create(func)
  end
  return pool
 end
 local function worker()
  while  do
  local task = coroutine.yield()
  print("Processing task: " .. task)
  end
 end
 local pool = createCoroutinePool(3, worker)
 -
 for i, co in ipairs(pool) do
  coroutine.resume(co, "Task " .. i)
 end
 -
 for i, co in ipairs(pool) do
  coroutine.resume(co, "Task " .. (i + 3))
 end

4. 高级特性

4.1 闭包

 function createCounter()
  local count = 0
  return function()
  count = count + 1
  return count
  end
 end
 local counter = createCounter()
 print(counter()) -- 1
 print(counter()) -- 2
 print(counter()) -- 3

4.2 模块系统

 -
 local M = {}
 function M.add(a, b)
  return a + b
 end
 function M.sub(a, b)
  return a - b
 end
 return M
 -
 local math = require("mymodule")
 print(math.add(10, 5)) -- 15
 print(math.sub(10, 5)) -- 5

4.3 元编程

 function createAccessor(obj, name)
  return function(value)
  if value ~= nil then
  obj[name] = value
  end
  return obj[name]
  end
 end
 local person = {}
 local name = createAccessor(person, "name")
 local age = createAccessor(person, "age")
 name("Alice")
 age(30)
 print(name()) -- Alice
 print(age()) -- 30

4.4 垃圾回收

 -
 local weakTable = setmetatable({}, { __mode = "k" })
 local key = {}
 weakTable[key] = "value"
 print(weakTable[key]) -- value
 key = nil -- 释放引用
 collectgarbage() -- 强制垃圾回收
 print(weakTable[key]) -- nil

5. 实战案例

5.1 事件系统

 local EventSystem = {}
 EventSystem.__index = EventSystem
 function EventSystem:new()
  local o = {}
  setmetatable(o, self)
  o.events = {}
  return o
 end
 function EventSystem:on(event, callback)
  if not self.events[event] then
  self.events[event] = {}
  end
  table.insert(self.events[event], callback)
 end
 function EventSystem:emit(event, ...)
  if self.events[event] then
  for _, callback in ipairs(self.events[event]) do
  callback(...)
  end
  end
 end
 function EventSystem:off(event, callback)
  if self.events[event] then
  for i, cb in ipairs(self.events[event]) do
  if cb == callback then
  table.remove(self.events[event], i)
  break
  end
  end
  end
 end
 -
 local events = EventSystem:new()
 -
 local function onUserLoggedIn(username)
  print("User logged in: " .. username)
 end
 events:on("userLoggedIn", onUserLoggedIn)
 -
 events:emit("userLoggedIn", "Alice") -- User logged in: Alice
 -
 events:off("userLoggedIn", onUserLoggedIn)
 events:emit("userLoggedIn", "Bob") -- 无输出

5.2 简单的类库

 -
 local Vector2 = {}
 Vector2.__index = Vector2
 function Vector2:new(x, y)
  local o = {}
  setmetatable(o, self)
  o.x = x or 0
  o.y = y or 0
  return o
 end
 function Vector2:add(other)
  return Vector2:new(self.x + other.x, self.y + other.y)
 end
 function Vector2:sub(other)
  return Vector2:new(self.x - other.x, self.y - other.y)
 end
 function Vector2:mul(scalar)
  return Vector2:new(self.x * scalar, self.y * scalar)
 end
 function Vector2:mag()
  return math.sqrt(self.x * self.x + self.y * self.y)
 end
 function Vector2:__tostring()
  return "Vector2(" .. self.x .. ", " .. self.y .. ")"
 end
 -
 Vector2.__add = Vector2.add
 Vector2.__sub = Vector2.sub
 Vector2.__mul = Vector2.mul
 -
 local v1 = Vector2:new(1, 2)
 local v2 = Vector2:new(3, 4)
 local v3 = v1 + v2
 print(v3) -- Vector2(4, 6)
 print(v3:mag()) -- 7.211102550928

6. 最佳实践

6.1 代码组织

  • 模块化: 将相关功能组织模块
  • 命名规范: 使用一致的命名约定
  • 代码风: 保持一致的缩进和代码风
  • 注释: 为复杂代码添加注释

6.2 性能优化

  • 避免全局变量: 使用局部变量访问速
  • 操作: 预分配大小,避免频繁扩容
  • 字符串操作: 避免频繁字符串连接,使用 table.concat
  • 垃圾回收: 合理使用弱表,避免内存泄漏

6.3 错误处理

  • 断言: 使用 assert 检查参数
  • 错误处理: 使用 pcall 捕获错误
  • 错误消息: 提供清晰的错误消息

6.4 调试技巧

  • 打印调试: 使用 print 或 io.write 输出调试信息
  • 调试器: 使用 Lua 调试器
  • 日志系统: 实现日志系统

7. 延伸阅读

8. 更新日志

  • 2026-04-05: 细化元表机制与协程原理
  • 2026-04-05: 扩展内容,增加面向对象编程、特性和实战案例

知识检测

学习进度

-- 已学文档
--% 知识覆盖率

学习推荐

专注模式