Lua極簡(jiǎn)入門(mén)(十)——面向?qū)ο?/h1>

在介紹完Lua的基礎(chǔ)知識(shí)包括元表,函數(shù)式編程之后,終于到了Lua面向?qū)ο缶幊獭km然并不打算使用Lua進(jìn)行大型應(yīng)用系統(tǒng)(程序)的開(kāi)發(fā)等脂,最多可能是嵌入到某個(gè)系統(tǒng)之間,如在Redis中使用Lua腳本完成一些操作撑蚌,或者使用Nginx+Lua完成服務(wù)限流或者日志收集上遥,負(fù)載均衡;另外比如我這里目前計(jì)劃使用Lua+Angular進(jìn)行一個(gè)Web前端的項(xiàng)目開(kāi)發(fā)争涌;這些工作仍然繞不開(kāi)面向?qū)ο蟆?/p>

面向?qū)ο缶幊蘋(píng)OP(Object Oriented Programming)粉楚,作為一種編程思想隨著互聯(lián)網(wǎng)的發(fā)展,已經(jīng)深入到現(xiàn)代系統(tǒng)編程的方方面面亮垫;OOP將對(duì)象作為程序的基礎(chǔ)單元模软,包含了數(shù)據(jù)和操作函數(shù);面向?qū)ο蟪绦蛟O(shè)計(jì)時(shí)饮潦,將業(yè)務(wù)中的事務(wù)進(jìn)行抽象封裝成多個(gè)對(duì)象燃异,程序在執(zhí)行時(shí),數(shù)據(jù)和消息將在每個(gè)對(duì)象間流轉(zhuǎn)继蜡,并按一定的操作流程依次執(zhí)行回俐。

類和實(shí)例是面向?qū)ο蟮闹匾拍罟渫龋瑢?duì)于Java、C#等面向?qū)ο笳Z(yǔ)言來(lái)說(shuō)仅颇,一般都具有關(guān)鍵詞class來(lái)標(biāo)識(shí)和定義一個(gè)類单默,并組織類模板;對(duì)于Lua來(lái)說(shuō)忘瓦,并不具備類的概念搁廓,table是Lua的最基礎(chǔ)對(duì)象,借助于tableLua也可以模擬類政冻,但每個(gè)對(duì)象需要自己定義行為和狀態(tài)枚抵。

Person對(duì)象為例,借助table很容易實(shí)現(xiàn)一個(gè)類明场,這個(gè)類具有兩個(gè)屬性,和一個(gè)方法李丰。

Person = { name = "ray", age = 0 }

Person.show = function(name, age)
    Person.name = name
    Person.age = age
    print("姓名:" .. Person.name .. ",年齡:" .. Person.age)
end
Person.show("ray", 12)
-->> 姓名:ray,年齡:12

在這段代碼中苦锨,看似實(shí)現(xiàn)了一個(gè)類,但其實(shí)只是對(duì)方法的一個(gè)封裝趴泌,無(wú)法從該類創(chuàng)建不同的實(shí)例舟舒,類只是模板,使用類模板嗜憔,可以創(chuàng)建出不同的實(shí)例秃励,是面向?qū)ο蟮闹饕卣鳌H缟鲜鍪纠罚绻凑認(rèn)ava的方法聲明對(duì)象:

person = Person -- 按照Lua的特點(diǎn)夺鲜,只是將person指向了Person,并沒(méi)有聲明實(shí)例
Person = nil    -- Person消亡呐舔,person也消亡
person.show("ray", 12)  -- 異常币励,說(shuō)明這并不是類,只是一個(gè)方法珊拼,只有一個(gè)對(duì)象的聲明周期
-->> attempt to index a nil value (global 'Person')

這個(gè)例子說(shuō)明按這種方式食呻,只是定義了一個(gè)方法。類是對(duì)事物的一種抽象澎现,如Person應(yīng)該是對(duì)的一種抽象仅胞,而應(yīng)用該類模板,可以聲明ray等等實(shí)例剑辫,其描述一個(gè)現(xiàn)實(shí)具體的人干旧,按這種方式理解,當(dāng)生命多個(gè)實(shí)例時(shí)揭斧,每個(gè)實(shí)例的聲明周期都是獨(dú)立的莱革,并不相互影響峻堰。Java中使用this來(lái)描述當(dāng)前實(shí)例,Lua中可以使用self作為接收者盅视,描述當(dāng)前實(shí)例對(duì)象捐名。

Person = { name = "ray", age = 0 }

Person.show = function(self, name, age)
    self.name = name
    self.age = age
    print("姓名:" .. self.name .. ",年齡:" .. self.age)
end

person = Person
Person = nil
person.show(person, "ray", 12)
-->> 姓名:ray,年齡:12

每個(gè)方法都放置self參數(shù)太麻煩了,Lua也可以像Java一樣闹击,編碼時(shí)對(duì)this實(shí)現(xiàn)隱藏镶蹋,Lua可以隱藏self參數(shù),實(shí)現(xiàn)在編碼時(shí)不必顯式聲明self赏半。Lua在聲明時(shí)贺归,使用:達(dá)到隱藏self的目的。

Person = {}

function Person:setInfo(name, age)
    self.name = name
    self.age = age
end
function Person:show()
    print("name:" .. self.name .. ", age: " .. self.age)
end

person = Person
Person:setInfo("ray", 12)
person:show()
-->> ray    12

使用:只是簡(jiǎn)化了顯式self參數(shù)的傳入断箫,包括調(diào)用和聲明時(shí)的傳遞拂酣,其他的和傳入self功能一致。如仲义,聲明時(shí)使用:婶熬,調(diào)用時(shí),使用.并傳入self效果一致埃撵。

-- 上例最后一步
person.show(person)
-->> ray    12

到這里L(fēng)ua使用table解決了類的獨(dú)立生命周期赵颅、隱藏self的問(wèn)題,但是目前編寫(xiě)的對(duì)象讓然不能稱之為暂刘,最基礎(chǔ)的饺谬,沒(méi)有辦法從上述定義中,獨(dú)立聲明多個(gè)實(shí)例谣拣。比如上例聲明了person對(duì)象后募寨,將無(wú)法再次聲明第二個(gè)實(shí)例。

person = Person
Person:setInfo("ray", 12)
person2 = Person
person2:setInfo("hh", 13)
person:show()
person2:show()
-->> name:hh, age: 13
-->> name:hh, age: 13

對(duì)于Java來(lái)說(shuō)芝发,類就是個(gè)抽象事物的模板绪商,使用new關(guān)鍵詞,可以創(chuàng)建任意的實(shí)例辅鲸,每一個(gè)實(shí)例都是具有模板中抽象的事務(wù)的獨(dú)立對(duì)象格郁。Lua由于沒(méi)有類的概念,使用table模擬類時(shí)独悴,如上例例书,聲明的對(duì)象將是同一個(gè)對(duì)象,這和類的表現(xiàn)不一致刻炒。為了解決獨(dú)立實(shí)例的問(wèn)題决采,只能自己定義類的形態(tài)和行為。

元表一章中坟奥,介紹過(guò)不同原型實(shí)現(xiàn)集成的功能树瞭,使用setmetatable拇厢、__index進(jìn)行元表設(shè)置,可以很容易的實(shí)現(xiàn)一個(gè)原型從另一個(gè)原型繼承晒喷。

當(dāng)訪問(wèn)一個(gè)table中的字段時(shí)孝偎,Lua會(huì)先從table中查找該字段,如果存在凉敲,則返回該字段的值衣盾;如果沒(méi)有,則檢查該table是否具有元表爷抓,如果沒(méi)有元表势决,則返回nil;如果有元表蓝撇,則會(huì)從元表中查找__index元方法果复,如果沒(méi)有該元方法,返回nil渤昌;如果有__index元方法据悔,則從該方法中查找指定字段。__index方法可以返回一個(gè)函數(shù)耘沼、也可以返回一個(gè)table

仍然使用上述示例,使用元表編程的方式朱盐,對(duì)這個(gè)Person對(duì)象進(jìn)行修改群嗤,提供一個(gè)類似Java的new實(shí)例的方法,當(dāng)創(chuàng)建一個(gè)新的對(duì)象時(shí)兵琳,將該對(duì)象繼承Person的所有對(duì)象及方法狂秘,通過(guò)setmetatable讓新對(duì)象的原型指向self,并設(shè)置__index索引也指向self躯肌。

Person = {}
function Person:new(p)
    -- 初始化者春,防止p(table)為空
    p = p or {}
    -- sefl為p的原型
    setmetatable(p, self)
    self.__index = self
    -- 返回創(chuàng)建的實(shí)例,此時(shí)p將具備Person的所有對(duì)象
    return p
end

function Person:show()
    print("name:" .. self.name .. ", age: " .. self.age)
end

person = Person:new({ name = "ray", age = 12 })
person2 = Person:new({ name = "hh", age = 13 })
person:show()
person2:show()
-->> name:ray, age: 12
-->> name:hh, age: 13

在本例中清女,當(dāng)創(chuàng)建一個(gè)對(duì)象時(shí)钱烟,person=Person:new,在該方法中嫡丙,設(shè)置了self為其元表(setmetatable(p, self))拴袭,即person的元表為Person;因此當(dāng)調(diào)用person:show()時(shí)曙博,其實(shí)際調(diào)用為person.show(person)拥刻,查找索引時(shí)會(huì)先從person的table中查找,未找到父泳,則查找__index條目般哼,上例中設(shè)置了self__index為self本身吴汪,此時(shí)__index的元表也是Person,那么此時(shí)的調(diào)用為Person.show(person)蒸眠,找到show方法并執(zhí)行漾橙。

將類的定義抽象,并劃定步驟黔宛,那么Lua在創(chuàng)建一個(gè)類時(shí)近刘,只需要兩步:

  • 創(chuàng)建一個(gè)基礎(chǔ)原型table
  • 創(chuàng)建一個(gè)實(shí)例化方法,并設(shè)置關(guān)聯(lián)元表以及__index
  • 其他的方法定義均為table:functionName
A = {}  -- 可具有默認(rèn)數(shù)據(jù)

function A:new(o)
    o = o or {}
    setmetatable(o, self)
    self.__index = self
    return o
end

類定義完成后臀晃,在訪問(wèn)屬性和方法時(shí)觉渴,.訪問(wèn)屬性,如A.b徽惋,:訪問(wèn)方法案淋,如A:function()

繼承

在面向?qū)ο缶幊讨校^承是另外一個(gè)非常重要的方面险绘,比如當(dāng)我們要定義各個(gè)品牌的汽車時(shí)踢京,哈弗、吉利宦棺、奇瑞等瓣距,汽車都是四個(gè)轱轆、四個(gè)門(mén)等代咸,我們自然會(huì)想到蹈丸,需要抽象出一個(gè)基礎(chǔ)類,讓其他品牌汽車都繼承基礎(chǔ)類呐芥。

以下步驟實(shí)現(xiàn)一個(gè)基礎(chǔ)的汽車對(duì)象逻杖,定義了四個(gè)車輪、四個(gè)車門(mén)和一個(gè)原型的方向盤(pán)思瘟,并提供了一個(gè)打印汽車基礎(chǔ)信息的方法:

Car = { wheel = 4, door = 4, steeringWheel = "circular" }

function Car:new(c)
    c = c or {}
    setmetatable(c, self)
    self.__index = self

    return c
end

function Car:showCarInfo()
    print("車輪:" .. self.wheel .. "荸百,車門(mén):" .. self.door .. ",方向盤(pán)形狀:" .. self.steeringWheel)
end

基礎(chǔ)類定義完成后滨攻,我們將重新定義一個(gè)新的跑車類够话,讓跑車集成汽車類,第一步铡买,先使用汽車類創(chuàng)建出一個(gè)默認(rèn)對(duì)象更鲁,并讓跑車指向該對(duì)象,此時(shí)奇钞,跑車將和轎車類具備一模一樣的方法澡为。

SportCar = Car:new()

s = SportCar:new()
s:showCarInfo()
-->> 車輪:4,車門(mén):4景埃,方向盤(pán)形狀:circular

如果只是改變汽車的基礎(chǔ)屬性媒至,或者是新增加新的屬性顶别,則可以直接使用new方法傳遞對(duì)象的方式實(shí)現(xiàn)即可,并不需要新增代碼拒啰,如跑車的車門(mén)數(shù)量為2驯绎,此時(shí)仍然使用基類的創(chuàng)建方法即可完成。

SportCar = Car:new()

s = SportCar:new { door = 2 }
s:showCarInfo()
-->> 車輪:4谋旦,車門(mén):2剩失,方向盤(pán)形狀:circular

為了更清晰的明確繼承和新類的定義方法,可以重寫(xiě)new函數(shù)册着,如下:

SportCar = Car:new()
function SportCar:new(s)
    s = s or Car:new(s)
    setmetatable(s, self)
    self.__index = self

    return s
end

s = SportCar:new { door = 2, steeringWheel = "Hexagon" } -- 方向盤(pán)為更酷的六邊形
s:showCarInfo()
-->> 車輪:4拴孤,車門(mén):2,方向盤(pán)形狀:Hexagon

實(shí)現(xiàn)基礎(chǔ)的對(duì)象繼承后甲捏,可以對(duì)新的跑車演熟,添加額外的方法,比如跑車的最高時(shí)速可達(dá)200公里司顿。

function SportCar:getMaxSpeed()
    return self.maxSpeed .. "公里"
end

s = SportCar:new { door = 2, steeringWheel = "Hexagon", maxSpeed = 200 }
print(s:getMaxSpeed())
-->> 200公里

面向?qū)ο缶幊讨忻⒋猓哂兄貙?xiě)方法的概念,對(duì)于實(shí)現(xiàn)了集成的Lua對(duì)象來(lái)說(shuō)大溜,也具備該功能化漆。我們實(shí)現(xiàn)了跑車類后,新增最高時(shí)速钦奋,那么基類中的展示汽車的基礎(chǔ)屬性方法顯然無(wú)法滿足我們的需求获三,此時(shí)可以重寫(xiě)該方法。

function SportCar:showCarInfo()
    print("跑車:車輪:" .. self.wheel .. "锨苏,車門(mén):" .. self.door .. ",方向盤(pán)形狀:" .. self.steeringWheel .. "棺聊,最高時(shí)速:" .. self:getMaxSpeed())
end

s = SportCar:new { door = 2, steeringWheel = "Hexagon", maxSpeed = 200 }
s:showCarInfo()
-->> 跑車:車輪:4伞租,車門(mén):2,方向盤(pán)形狀:Hexagon限佩,最高時(shí)速:200公里

至此類的繼承已經(jīng)實(shí)現(xiàn)完成葵诈,將上述散亂的代碼合并在一起,如下:

Car = { wheel = 4, door = 4, steeringWheel = "circular" }

function Car:new(c)
    c = c or {}
    setmetatable(c, self)
    self.__index = self

    return c
end

function Car:showCarInfo()
    print("車輪:" .. self.wheel .. "祟同,車門(mén):" .. self.door .. "作喘,方向盤(pán)形狀:" .. self.steeringWheel)
end

SportCar = Car:new()
function SportCar:new(s)
    s = s or Car:new(s)
    setmetatable(s, self)
    self.__index = self

    return s
end

function SportCar:getMaxSpeed()
    return self.maxSpeed .. "公里"
end

function SportCar:showCarInfo()
    print("跑車:車輪:" .. self.wheel .. ",車門(mén):" .. self.door .. "晕城,方向盤(pán)形狀:" .. self.steeringWheel .. "泞坦,最高時(shí)速:" .. self:getMaxSpeed())
end

c = Car:new()
s = SportCar:new { door = 2, steeringWheel = "Hexagon", maxSpeed = 200 }
c:showCarInfo()
s:showCarInfo()
-->> 車輪:4,車門(mén):4砖顷,方向盤(pán)形狀:circular
-->> 跑車:車輪:4贰锁,車門(mén):2赃梧,方向盤(pán)形狀:Hexagon,最高時(shí)速:200公里

訪問(wèn)限制

訪問(wèn)限制是面向?qū)ο蟮牧硗庖粋€(gè)方面豌熄,對(duì)于Java來(lái)說(shuō)授嘀,可以通過(guò)privateprotected锣险、public很容易實(shí)現(xiàn)訪問(wèn)權(quán)限控制蹄皱,而對(duì)于Lua來(lái)說(shuō),類都是不具備的芯肤,私密控制同樣沒(méi)有巷折;Lua是使用table進(jìn)行的模擬實(shí)現(xiàn)類,那么和Lua閉包相結(jié)合纷妆,也可以實(shí)現(xiàn)私密訪問(wèn)盔几。

function Car()
    local _M = {
        wheel = 4, door = 4, steeringWheel = "circular"
    }
    function _M:new(c)
        c = c or {}
        setmetatable(c, self)
        for k, v in pairs(self) do
            if not o[k] then
                o[k] = v
            end
        end
        self.__index = self

        return c
    end
    local function run()
        print("普通轎車,100公里每小時(shí)速度進(jìn)行行駛")
    end
    function _M:showCarInfo()
        print("車輪:" .. self.wheel .. "掩幢,車門(mén):" .. self.door .. "逊拍,方向盤(pán)形狀:" .. self.steeringWheel)
        run()
    end
    return _M
end

c = Car()
c:showCarInfo()
c.run() -- 外部無(wú)法使用
-->> 車輪:4,車門(mén):4际邻,方向盤(pán)形狀:circular
-->> 普通轎車芯丧,100公里每小時(shí)速度進(jìn)行行駛

這種方式的實(shí)現(xiàn)原理是采用了兩個(gè)元表,公開(kāi)的方法世曾,都放入到_M元表中缨恒,并于最后返回,不公開(kāi)的方法轮听,都存儲(chǔ)在本身元表中骗露。

一般情況下,對(duì)于模塊(類)的定義可以固化為如下形式

local _M = {
    _VERSION = "1.0",
    _NAME = "Http 方法封裝"
}

-- 1. 私有方法放置在這里
local function joinParam(param)
    local str = ""
    for i, v in pairs(param) do
        if str ~= "" then
            str = str .. "&"
        end
        str = str .. i .. "=" .. v
    end

    return str
end
local function request(url, param, method)
    return "向" .. url .. "發(fā)起" .. method .. "方法血巍,傳遞參數(shù):" .. joinParam(param)
end

-- 2. new方法
function _M:new()
    local o = o or {}
    setmetatable(o, self)
    for k, v in pairs(self) do
        if not o[k] then
            o[k] = v
        end
    end
    self.__index = self

    return o
end

-- 3. 公開(kāi)的方法
function _M:get(url, param)
    return request(url, param, "GET")
end

-- 4. 返回_M對(duì)象
return _M

在其他類中引用該對(duì)象發(fā)起http請(qǐng)求

-- testHttp為上述類的文件名萧锉,如果有路徑也需要定義,如path.fileName
local http = require("testHttp"):new() 

local content = http:get("http://baidu.com", { uid = "ray", pwd = "111111" })
print(content)
-->> 向http://baidu.com發(fā)起GET方法述寡,傳遞參數(shù):uid=ray&pwd=111111
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者

  • 序言:七十年代末柿隙,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子鲫凶,更是在濱河造成了極大的恐慌禀崖,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,743評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件螟炫,死亡現(xiàn)場(chǎng)離奇詭異波附,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,296評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén)叶雹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)财饥,“玉大人,你說(shuō)我怎么就攤上這事折晦≡啃牵” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,285評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵满着,是天一觀的道長(zhǎng)谦炒。 經(jīng)常有香客問(wèn)我,道長(zhǎng)风喇,這世上最難降的妖魔是什么宁改? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,485評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮魂莫,結(jié)果婚禮上还蹲,老公的妹妹穿的比我還像新娘。我一直安慰自己耙考,他們只是感情好谜喊,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,581評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著倦始,像睡著了一般斗遏。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上鞋邑,一...
    開(kāi)封第一講書(shū)人閱讀 49,821評(píng)論 1 290
  • 那天诵次,我揣著相機(jī)與錄音,去河邊找鬼枚碗。 笑死逾一,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的肮雨。 我是一名探鬼主播嬉荆,決...
    沈念sama閱讀 38,960評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼酷含!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起汪茧,我...
    開(kāi)封第一講書(shū)人閱讀 37,719評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤椅亚,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后舱污,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體呀舔,經(jīng)...
    沈念sama閱讀 44,186評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,516評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了媚赖。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片霜瘪。...
    茶點(diǎn)故事閱讀 38,650評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖惧磺,靈堂內(nèi)的尸體忽然破棺而出颖对,到底是詐尸還是另有隱情,我是刑警寧澤磨隘,帶...
    沈念sama閱讀 34,329評(píng)論 4 330
  • 正文 年R本政府宣布缤底,位于F島的核電站,受9級(jí)特大地震影響番捂,放射性物質(zhì)發(fā)生泄漏个唧。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,936評(píng)論 3 313
  • 文/蒙蒙 一设预、第九天 我趴在偏房一處隱蔽的房頂上張望徙歼。 院中可真熱鬧,春花似錦鳖枕、人聲如沸魄梯。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,757評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)画恰。三九已至,卻和暖如春吸奴,著一層夾襖步出監(jiān)牢的瞬間允扇,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,991評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工则奥, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留考润,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,370評(píng)論 2 360
  • 正文 我出身青樓读处,卻偏偏與公主長(zhǎng)得像糊治,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子罚舱,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,527評(píng)論 2 349

推薦閱讀更多精彩內(nèi)容