<返回目录     Powered by claud/xia兄

第12课: 面向对象编程

基础类实现

Lua面向对象原理:

Lua本身不支持传统的面向对象编程,但通过表和元表可以完美模拟。核心机制是使用__index元方法实现方法查找和继承。当访问对象的方法时,Lua首先在对象自身查找,如果找不到则查找元表的__index字段。通过将__index设置为父类,可以实现继承链。冒号语法(obj:method())是语法糖,等价于obj.method(obj),自动传递self参数。

-- 使用table和元表实现类
Person = {}
Person.__index = Person

function Person:new(name, age)
    local obj = {}
    setmetatable(obj, Person)
    obj.name = name
    obj.age = age
    return obj
end

function Person:sayHello()
    print("你好,我是" .. self.name .. ",今年" .. self.age .. "岁")
end

-- 使用类
local p1 = Person:new("张三", 25)
p1:sayHello()  -- 输出: 你好,我是张三,今年25岁

-- 理解冒号语法
p1:sayHello()  -- 等价于 Person.sayHello(p1)
p1.sayHello(p1)  -- 同样的效果

构造函数优化

-- 更完善的类定义
function Person:new(o)
    o = o or {}
    setmetatable(o, self)
    self.__index = self
    return o
end

-- 使用
local p = Person:new{name = "李四", age = 30}
p:sayHello()

继承实现

-- 父类
Animal = {}
Animal.__index = Animal

function Animal:new(name)
    local obj = {name = name}
    setmetatable(obj, Animal)
    return obj
end

function Animal:speak()
    print(self.name .. " 发出声音")
end

-- 子类继承
Dog = {}
setmetatable(Dog, {__index = Animal})
Dog.__index = Dog

function Dog:new(name, breed)
    local obj = Animal:new(name)
    setmetatable(obj, Dog)
    obj.breed = breed
    return obj
end

function Dog:speak()
    print(self.name .. " 汪汪叫")
end

function Dog:fetch()
    print(self.name .. " 去捡球")
end

-- 使用
local dog = Dog:new("旺财", "金毛")
dog:speak()  -- 输出: 旺财 汪汪叫
dog:fetch()  -- 输出: 旺财 去捡球

多态实现

-- 定义多个子类
Cat = {}
setmetatable(Cat, {__index = Animal})
Cat.__index = Cat

function Cat:new(name, color)
    local obj = Animal:new(name)
    setmetatable(obj, Cat)
    obj.color = color
    return obj
end

function Cat:speak()
    print(self.name .. " 喵喵叫")
end

-- 多态演示
local animals = {
    Dog:new("旺财", "金毛"),
    Cat:new("咪咪", "白色"),
    Dog:new("大黄", "土狗")
}

for _, animal in ipairs(animals) do
    animal:speak()  -- 根据实际类型调用不同方法
end

私有成员

-- 使用闭包实现私有成员
function BankAccount(initialBalance)
    local balance = initialBalance  -- 私有变量

    local obj = {}

    function obj:deposit(amount)
        if amount > 0 then
            balance = balance + amount
            return true
        end
        return false
    end

    function obj:withdraw(amount)
        if amount > 0 and amount <= balance then
            balance = balance - amount
            return true
        end
        return false
    end

    function obj:getBalance()
        return balance
    end

    return obj
end

-- 使用
local account = BankAccount(1000)
account:deposit(500)
print(account:getBalance())  -- 1500
-- print(account.balance)  -- nil,无法直接访问

属性访问控制

-- 使用元方法控制属性访问
function createClass()
    local private = {}
    local public = {}

    public.__index = function(t, k)
        return private[k] or public[k]
    end

    public.__newindex = function(t, k, v)
        if k:sub(1, 1) == "_" then
            error("不能设置私有属性: " .. k)
        end
        private[k] = v
    end

    return setmetatable({}, public)
end

local obj = createClass()
obj.name = "张三"  -- 正常
-- obj._private = "test"  -- 报错

类方法和实例方法

Calculator = {}
Calculator.__index = Calculator
Calculator.count = 0  -- 类变量

function Calculator:new()
    local obj = {}
    setmetatable(obj, Calculator)
    Calculator.count = Calculator.count + 1
    return obj
end

-- 实例方法
function Calculator:add(a, b)
    return a + b
end

-- 类方法
function Calculator.getCount()
    return Calculator.count
end

-- 使用
local c1 = Calculator:new()
local c2 = Calculator:new()
print(Calculator.getCount())  -- 2
print(c1:add(5, 3))  -- 8

接口模拟

-- 定义接口
Drawable = {}

function Drawable:draw()
    error("必须实现draw方法")
end

-- 实现接口
Circle = {}
setmetatable(Circle, {__index = Drawable})
Circle.__index = Circle

function Circle:new(radius)
    local obj = {radius = radius}
    setmetatable(obj, Circle)
    return obj
end

function Circle:draw()
    print("绘制半径为" .. self.radius .. "的圆")
end

-- 使用
local circle = Circle:new(5)
circle:draw()

运算符重载

Vector = {}
Vector.__index = Vector

function Vector:new(x, y)
    local obj = {x = x, y = y}
    setmetatable(obj, Vector)
    return obj
end

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

-- 重载减法
function Vector.__sub(v1, v2)
    return Vector:new(v1.x - v2.x, v1.y - v2.y)
end

-- 重载乘法(标量)
function Vector.__mul(v, scalar)
    if type(v) == "number" then
        v, scalar = scalar, v
    end
    return Vector:new(v.x * scalar, v.y * scalar)
end

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

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

混入(Mixin)模式

-- 定义混入
Serializable = {}

function Serializable:serialize()
    local result = {}
    for k, v in pairs(self) do
        if type(v) ~= "function" then
            table.insert(result, k .. "=" .. tostring(v))
        end
    end
    return table.concat(result, ",")
end

-- 应用混入
function mixin(target, source)
    for k, v in pairs(source) do
        if target[k] == nil then
            target[k] = v
        end
    end
end

-- 使用
User = {}
User.__index = User

function User:new(name, email)
    local obj = {name = name, email = email}
    setmetatable(obj, User)
    return obj
end

mixin(User, Serializable)

local user = User:new("张三", "zhang@example.com")
print(user:serialize())  -- name=张三,email=zhang@example.com

单例模式

-- 单例实现
Singleton = {}
local instance = nil

function Singleton:getInstance()
    if not instance then
        instance = {}
        setmetatable(instance, {__index = Singleton})
        instance:init()
    end
    return instance
end

function Singleton:init()
    self.data = {}
end

function Singleton:setData(key, value)
    self.data[key] = value
end

function Singleton:getData(key)
    return self.data[key]
end

-- 使用
local s1 = Singleton:getInstance()
local s2 = Singleton:getInstance()
s1:setData("name", "张三")
print(s2:getData("name"))  -- 张三
print(s1 == s2)  -- true

工厂模式

-- 产品类
Button = {}
Button.__index = Button

function Button:new(text)
    local obj = {text = text}
    setmetatable(obj, Button)
    return obj
end

function Button:render()
    print("渲染按钮: " .. self.text)
end

Input = {}
Input.__index = Input

function Input:new(placeholder)
    local obj = {placeholder = placeholder}
    setmetatable(obj, Input)
    return obj
end

function Input:render()
    print("渲染输入框: " .. self.placeholder)
end

-- 工厂
UIFactory = {}

function UIFactory.create(type, ...)
    if type == "button" then
        return Button:new(...)
    elseif type == "input" then
        return Input:new(...)
    end
end

-- 使用
local btn = UIFactory.create("button", "提交")
local input = UIFactory.create("input", "请输入用户名")
btn:render()
input:render()
重要提示:
练习题:
  1. 实现一个Shape基类和Circle、Rectangle子类
  2. 创建一个支持链式调用的QueryBuilder类
  3. 实现观察者模式
  4. 设计一个游戏角色类系统(战士、法师、弓箭手)