• 在lua中创建字段安全的对象


      lua萌新,刚刚学习和使用不到一个月。有不对的地方,还望各路大神不吝赐教。

      lua中可以用table来模拟对象,但table是可以任意增加键值的。在对象模拟中,暂且也叫它为字段(field)吧。如果在面向对象中,你定义了一个对象,可以在对象以外的地方随意改动这个对象的字段,访问不存在的字段,你想象一下这有多恐怖?比如你定义了一个Vector3{float x = 0; float y = 0; float z = 0;}  我在外面某处加一个float t = 1; 当你在创建并引用这对象的时候,你就懵逼了,t是什么鬼?又或者你new 一个对象 vector,然后去取一个值,本来里面没有 t 这个字段,vector.t 给你返回一个空值,你是不是又懵逼了?到底是里面有这个字段值为空,还是压根就没这个字段?嗯? 感觉太不可控了。

      那么,有没有办法让它可控一点呢?有的。利用元表的__index 和__newindex,具体如下代码:

    local Vector3 = {}
    function Vector3:new()
        local v3 = 
        {
            x = 0,   -- Note:这个值不能为nil,不然找不到这个字段
            y = 0,
            z = 0,
        }
    
        setmetatable(v3, self)
        self.__index = 
            function(self, key)
                error("Vector3类型中没有定义字段:" .. key, 2)
            end
        self.__newindex = 
            function(self, key, value)
                error("Vector3类型中没有定义字段:" .. key, 2)
              end
        return v3
    end
    
    local v  = Vector3:new()
    v.x = 2
    --v.t = 3
    print(v.x)
    print(v.y)
    --print(v.t) 

    上面的代码输出:

    但当你尝试把v.t = 3 的注释去掉的话,就报错了:

    尝试去掉print(v.t)  的注释的话,也会报错:

      这样就可以确保这个结构的安全,主要体现在不能在外部随意对它修改。

       --------------------2017.08.20更新

      下面是升级版本的,初始化的时候可以传入参数,为初始化提供了方便。还提供了一个ToString()方法。

    local Vector3 = {}
    function Vector3:new(x0, y0, z0)
        local v3 = 
        {
            x = x0 or 0,
            y = y0 or 0,
            z = z0 or 0,
    
            ToString = function(self)
                return "(" .. self.x .. "," .. self.y .. "," .. self.z  .. ")"
            end
        }
    
        setmetatable(v3, self)
        self.__index = 
            function(self, key)
                error("Vector3类型中没有定义字段:" .. key, 2)
            end
        self.__newindex = 
            function(self, key, value)
                error("Vector3类型中没有定义字段:" .. key, 2)
              end
        return v3
    end
    
    local v  = Vector3:new(3, 3, 3)
    v.x = 2
    print(v.x)
    print(v.y)
    print("Vector3 v = " .. v:ToString())

      --------------------2017.08.22更新

      上面的方法依然会有问题,就是当给字段赋值为空的时候,相当于把字段删了,字段就消失了。不能再次访问或者赋值。下面是再次升级的版本:

    local Vector3 = {}
    function Vector3:new(x0, y0, z0)
        --合法字段和默认值
        local v3_field = 
        {
            x = 0, 
            y = 0,
            z = 0,
        }
        --初始化字段
        local v3 = 
        {
            x = x0 or v3_field.x,
            y = y0 or v3_field.y,
            z = z0 or v3_field.z,
        }
    
        setmetatable(v3, self)
        self.__tostring = function(self)
                return "(" .. self.x .. "," .. self.y .. "," .. self.z  .. ")"
            end
        self.__index = 
            function(self, key)
                local valid = false
                for k, v in pairs(v3_field) do 
                    if key == k then
                        valid = true
                        result = rawget(v3, key) or v --如果值为空,返回v3_field中的默认值
                        return result
                    end
                end
                if not valid then
                    error("Invalid field in Vector3: " .. key, 2)
                end
            end
        self.__newindex = 
            function(self, key, value)
                print("set newindex")
                local valid = false
                for k, v in ipairs(v3_field) do 
                    if key == k then
                        valid = true
                        rawset(v3, key, value)
                        return
                    end
                end
                if not valid then
                    error("Invalid field in Vector3: " .. key, 2)
                end
              end
        return v3
    end
    
    local v  = Vector3:new(3, 3, 3)
    v.x = 2
    print(v.x)
    v.y = nil
    print(v.y)
    print("Vector3 v = " .. tostring(v))

    输出为:

    这样即使是字段被赋值为nil,依然可以访问并重新赋值。

  • 相关阅读:
    代理模式
    组合模式
    策略模式
    状态模式
    js 未结束的字符串常量错误解决方法
    struts2+hibernate+poi导出Excel实例
    Java 实现导出excel表 POI
    ExtJS 4.2 中自定义事件
    dhtmlxGrid分页查询,条件查询实例
    '@P0' 附近有语法错误
  • 原文地址:https://www.cnblogs.com/yougoo/p/7398194.html
Copyright © 2020-2023  润新知