On this page

Lua 面向对象

Lua 面向对象编程(OOP)完整指南

Lua 虽然本身不是面向对象语言,但通过 table 和 metatable 可以轻松实现 OOP 特性。以下是 Lua 中实现面向对象编程的多种方式:

1. 基础对象实现

简单对象

-- 创建对象
local dog = {
    name = "Buddy",
    age = 3
}

-- 对象方法
function dog:bark()
    print(self.name .. " says: Woof!")
end

-- 使用
dog:bark()  -- 输出: Buddy says: Woof!

2. 类与实例

原型式继承

-- 定义"类"
local Animal = {
    name = "Unknown",
    age = 0
}

-- 类方法
function Animal:speak()
    print(self.name .. " makes a sound")
end

-- 构造函数
function Animal:new(name, age)
    local obj = {
        name = name or self.name,
        age = age or self.age
    }
    setmetatable(obj, {__index = self})  -- 设置原型
    return obj
end

-- 创建实例
local cat = Animal:new("Whiskers", 2)
cat:speak()  -- 输出: Whiskers makes a sound

3. 继承实现

单继承

-- 派生类
local Dog = Animal:new()  -- 继承Animal

-- 重写方法
function Dog:speak()
    print(self.name .. " says: Woof!")
end

-- 新增方法
function Dog:fetch()
    print(self.name .. " fetches the stick")
end

-- 使用
local myDog = Dog:new("Rex", 4)
myDog:speak()  -- 输出: Rex says: Woof!
myDog:fetch()  -- 输出: Rex fetches the stick

4. 多继承模拟

-- 第二个基类
local Flyable = {
    canFly = false
}

function Flyable:fly()
    if self.canFly then
        print(self.name .. " is flying")
    else
        print(self.name .. " cannot fly")
    end
end

-- 多继承函数
local function multiInherit(...)
    local classes = {...}
    local instance = {}
    
    -- 设置多个__index
    setmetatable(instance, {
        __index = function(table, key)
            for _, class in ipairs(classes) do
                local value = class[key]
                if value then return value end
            end
        end
    })
    
    return instance
end

-- 使用多继承
local SuperDog = multiInherit(Dog, Flyable)
local superDog = SuperDog:new("Krypto", 5)
superDog.canFly = true
superDog:speak()  -- 从Dog继承
superDog:fly()    -- 从Flyable继承

5. 封装与私有成员

使用闭包实现私有性

local function createPerson(name)
    -- 私有变量
    local age = 0
    
    -- 创建对象
    local obj = {
        name = name,
        getAge = function(self) return age end,
        birthday = function(self)
            age = age + 1
            print(self.name .. " is now " .. age .. " years old")
        end
    }
    
    return obj
end

local p = createPerson("Alice")
p.birthday(p)  -- 输出: Alice is now 1 years old
print(p.age)   -- 输出: nil (无法直接访问)

6. 操作符重载

通过元表重载操作符

local Vector = {}
Vector.__index = Vector

function Vector:new(x, y)
    return setmetatable({x = x or 0, y = y or 0}, self)
end

-- 重载加法
function Vector.__add(a, b)
    return Vector:new(a.x + b.x, a.y + b.y)
end

-- 重载tostring
function Vector.__tostring(v)
    return string.format("Vector(%f, %f)", v.x, v.y)
end

-- 使用
local v1 = Vector:new(1, 2)
local v2 = Vector:new(3, 4)
local v3 = v1 + v2
print(v3)  -- 输出: Vector(4.000000, 6.000000)

7. 类系统库

使用 middleclass 等第三方库

-- 安装: luarocks install middleclass
local class = require 'middleclass'

-- 定义类
local Animal = class('Animal')

function Animal:initialize(name)
    self.name = name
end

function Animal:speak()
    print(self.name .. " makes a sound")
end

-- 继承
local Dog = class('Dog', Animal)

function Dog:speak()
    print(self.name .. " says woof!")
end

-- 使用
local d = Dog:new('Buddy')
d:speak()  -- 输出: Buddy says woof!

8. 最佳实践

  1. 命名约定

    • 类名使用 PascalCase (如 MyClass)
    • 实例和方法使用 camelCase (如 myInstance:doSomething())
  2. 性能优化

    -- 将常用方法局部化
    local setmetatable = setmetatable
    local print = print
    
  3. 内存管理

    • 使用 __gc 元方法清理资源
    • 注意循环引用问题
  4. 文档规范

    --- 表示一个人的类
    -- @class Person
    -- @field name string 姓名
    local Person = {}
       
    --- 构造函数
    -- @string name 姓名
    -- @int age 年龄
    -- @treturn Person 新实例
    function Person:new(name, age)
        -- 实现...
    end
    

9. 设计模式实现

单例模式

local Singleton = {}
setmetatable(Singleton, {
    __call = function(cls)
        if not cls.instance then
            cls.instance = setmetatable({}, {__index = cls})
        end
        return cls.instance
    end
})

function Singleton:sayHello()
    print("Hello from Singleton")
end

-- 使用
local s1 = Singleton()
local s2 = Singleton()
print(s1 == s2)  -- 输出: true

观察者模式

local Subject = {}

function Subject:new()
    local obj = {
        observers = {}
    }
    setmetatable(obj, {__index = Subject})
    return obj
end

function Subject:subscribe(observer)
    table.insert(self.observers, observer)
end

function Subject:notify(message)
    for _, observer in ipairs(self.observers) do
        observer:update(message)
    end
end

-- 使用
local subject = Subject:new()

local observer1 = {
    update = function(self, msg)
        print("Observer1 received: " .. msg)
    end
}

subject:subscribe(observer1)
subject:notify("Test message")

Lua 的面向对象实现虽然不像传统 OOP 语言那样有原生支持,但这种灵活性允许开发者根据项目需求选择最适合的实现方式。无论是简单的原型模式还是复杂的类系统,Lua 都能优雅地实现。