zoukankan      html  css  js  c++  java
  • lua 面向对象

    对象的特点是:

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

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

    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")
  • 相关阅读:
    Google搜索引擎如何运作:不会手动调整结果
    一个Ruby脚本
    IE灭绝!?
    除Windows之外的其他操作系统
    刚收到的新书
    奇怪的计算机语言
    小巧的menuetOS
    Ruby学习笔记(1)
    一个通知
    总结 asp.net 和 javascript获取本地IP(MAC)和服务器IP(MAC)的方法
  • 原文地址:https://www.cnblogs.com/iRidescent-ZONE/p/5650797.html
Copyright © 2011-2022 走看看