黑铁时代
Programing is not only one kind of technology, but also one kind of art.

对象的特点是:

  对象拥有自己的数据,两个对象即使数据完全相同,他们也是相互独立的;

  对象之间可以共享对象的行为,也就是他们的方法是一致的;

 

lua中的table就非常适合作为一个对象,可以在table中方便的定义属性和方法:

  Point = { x = 0, y = 0 }

  Point.set = function (x, y)

    Point.x = x

    Point.y = y

  end

  Point.set(1, 2)

但是要符合面相对象的规则,方法可以操作不同的对象,而不是同一个特定对象,我们可以将需要操作的对象作为参数传入:

  Point = {}

  Point.set = function (pt, x, y)

      pt.x = x

      pt.y = y

    end

  local pt1 = { x = 0, y = 0 }

  Point.set(pt, 1, 2)

我们可以将所有方法定义在一个集合中,然后在调用的时候将第一个参数作为对象传入即可。不过传统的面向对象是可以隐藏对象这个参数,而直接使用this代替,lua也提供了一种类似的操作,就是“:”操作符:

  Point = {}

  function Point:set (x, y) // 等价于 function Point.set(self, x, y)

    self.x = x

    self.y = y

  end

  Point:set(1, 2) // 等价于Point.set(Point, 1, 2)

这里冒号操作符的作用就是省略了第一个参数self,self是代表冒号之前的那个对象。.操作符和:操作符是可以随意互换的,只要在调用的时候确定参数的正确性即可,因为冒号操作符并没有引入新的东西,只是一种“语法糖”而已。

 

类是创建对象的模版,同一个类型的所有对象都具有相同的行为。

在lua中,如何创建一个类?lua中使用table来创建一个类,然后所有实例的元表都可以设置为这个类,即相同类型的对象具有相同的元表:

创建一个Point类:

  Point = {}

  function Point.new (x, y)

    local instance = {} // 创建一个实例

    setmetatable(instance, Point) // 设置所有实例的元表都是Point,所以实例的方法都会去Point中查找

    Point.__index = Point // 同时必须设置__index元方法为Point,因为实例的所有方法,我们都会定义在Point中

    instance.x = x

    instance.y = y

    return instance

  end

添加类的方法:

  function Point:set (x, y)

    self.x = x

    self.y = y

  end

  function Point:get ()

    return {x, y}

  end

使用:

  local pt = Point.new(1, 2)

  pt:set(3, 4)

  local data = pt:get()

类的继承:根据lua的table特性,如果一个字段在table中不存在,那么lua回去尝试调用元表的__index元方法,如果元方法是一个table,那么该规则会持续下去,直到找到一个定义或者搜索结束。这种机制就类似一个链式结构,这个链式结构就形成了一个继承关系。子类不存在某个方法的时候,总是会向父类去寻找,依次类推。

从Point类继承一个子类Point3D:

  Point3D = {}

  function Point3D:new (x, y, z)

    local instance = Point:new(x, y) // 创建一个父类对象,继承了父类所有的属性

    instance.z = z

    setmetatable(Point3D, Point) // 设置子类Point3D的元表为父类Point

    Point3D.__index = Point3D // 设置元方法为自己

    setmetatable(instance. Point3D) // 设置instance的元表为Point3D

    return instance  

  end

重写父类的方法:

  function Point3D:set (x, y, z)

    Point.set(self, x, y)

    self.z = z

  end

实现自己的方法:

  function Point3D:setZ (z)

    self.z = z

  end

甚至为instace实现特有的方法:

   function instance:print()

    print("it is a printer for instance ", self)

  end

使用:

  local pt3d = Point3D:new(1, 2, 3)

  pt3d:set(4, 5, 6)

  pt3d:setZ(10)

  pt3d:print()

这样实现后,就形成了instance -> Point3D -> Point 的链式结构,lua会按照这个顺序依此搜索方法,调用最先找到的方法。

实例:

Point.lua

--#! /usr/local/bin/lua

Point = {}
Point.__index = Point;

local POINT_PROPS = {
    x = 0,
    y = 0
}

function Point:new(x, y)
    local instance = {}
    for k, v in pairs(POINT_PROPS) do
        instance[k] = v
    end
    instance.x = x
    instance.y = y

    setmetatable(instance, Point)

    return instance
end

function Point:set (x, y)
    print("Point set x:"..x.." y:"..y)

    self.x = x
    self.y = y
end

function Point.get (self)
    print("Point get x:"..self.x.." y:"..self.y)

    return {x, y} 
end

Point3D.lua

--#! /usr/local/bin/lua

require "Point"

Point3D = {}
Point3D.__index = Point3D;

local POINT3D_PROPS = {
    z = 0
}

function Point3D:new (x, y, z)
    local instance = Point:new(x, y)
    for k, v in pairs(POINT3D_PROPS) do
        instance[k] = v
    end
    instance.z = z

    setmetatable(Point3D, Point)
    setmetatable(instance, Point3D)

    return instance
end

function Point3D:set (x, y, z)
    print("Point3D set x:"..x.." y:"..y.." z:"..z)

    Point.set(self, x, y)
    self.z = z
end

function Point3D:setZ (z)
    print("Point3D setZ z:"..z)

    self.z = z
end

function Point3D:get ()
    print("Point3D get x:"..self.x.." y:"..self.y.." z:"..self.z)
end

test.lua

require "Point3D"

local pt = Point3D:new(3, 5, 8);
for k, v in pairs(pt) do
    print(k, v)
end
pt:set(1, 2, 3)
pt:get()

function pt:print()
    print("pt", self.x, self.y, self.z, self)
end

pt:print()

 

多重继承:

  为了实现多重继承,就无法为对象设置一个父类作为元表,必须将多个父类保存起来,然后设置一个元方法__index为函数,在函数调用的时候,在多个父类中为对象寻找适合的方法。

Color.lua

Color = {}

function Color:setColor (r, g, b, a)
    self.r = r
    self.g = g
    self.b = b
    self.a = a
end

function Color:getColor ()
    print("color r:"..self.r.." g:"..self.g.." b:"..self.b.." a"..self.a)

    return {self.r, self.g, self.b, self.a} 
end

test.lua

require "Point"
require "Color"

ColorPoint = {}
ColorPoint.__interfaces = { Point, Color }
ColorPoint.__index = function (t, key)
    local interfaces = ColorPoint.__interfaces;

    for i = 1, #interfaces do
        local func = interfaces[i][key]
        if func then return func end
    end
    return nil
end

function ColorPoint:new ()
    local instance = {}
    setmetatable(instance, ColorPoint)
    return instance
end

local cp = ColorPoint:new()
local cp1 = ColorPoint:new()

cp:set(1, 2)
cp:get()
cp:setColor(34, 12, 43, 43)
cp:getColor()

 

单列模式,利用了闭包的原理,将数据隐蔽起来:

local _instance = {}

local filelist = {}
local fileCount = 0

local isExist = function (filename)
    return not (filelist[filename] == nil)
end

function _instance:load (filename)
    if (isExist(filename)) then print(filename) return end

    local file = io.open(filename)

    if (file) then
        io.input(file)
        local data = assert(io.read("*all"))
        filelist[filename] = data

        fileCount = fileCount + 1

        io.close(file)
    else
        error("FileManager load file "..filename.." failed")
    end
end

function _instance:getFileCount ()
    return fileCount
end

function _instance:getFileData (filename)
    return filelist[filename]
end

function _instance:removeFileData (filename)
    if (filelist[filename]) then
        filelist[filename] = nil
        fileCount = fileCount - 1
    end

    assert(fileCount >= 0, "FileManager removeFileData file count error")
end

function _instance:isExist (filename)
    return not (filelist[filename] == nil)
end

FileManager = {
    getInstance = function()
        return _instance
    end
}
_G["FileManager"] = FileManager
return FileManager
FileManager:getInstance():load("1.txt")
FileManager:getInstance():load("2.txt")
print(FileManager:getInstance():getFileCount())
print(FileManager:getInstance():getFileData("1.txt"))
FileManager:getInstance():removeFileData("1.txt")
print(FileManager:getInstance():getFileCount())
print(FileManager:getInstance():getFileData("1.txt"))
print(FileManager:getInstance():getFileData("2.txt")

 

posted on 2016-07-07 17:03  黑铁时代  阅读(648)  评论(0编辑  收藏  举报