zoukankan      html  css  js  c++  java
  • Lua面向对象设计(转)

      首先对于Lua语言,它没有打算被用来进行大型的程序设计,相反,Lua目标定于小型到中型的程序设计,通常是作为大型系统的一部分,所以它只提供了一套精简的元素,很多高级语言的概念都没有。这样Lua就成为了一个既简单又灵活的轻量级语言,但是基本上高级语言中的大多数机制都可以在现有Lua的基础上加以实现。

      面向对象的基础是类,但Lua中没有提供类的概念,所以我们需要利用Lua现有的机制来实现类似于类的有关oop的一整套概念。基本方案是使用table来实现类机制,并且结合使用self参数和冒号操作。我们先来看看self参数和冒号操作符的用法:

      self参数的使用是很多面向对象语言的要点,大多数OO语言将这种机制隐藏起来,这样程序员不必声明这个参数(虽然仍然可以在方法内使用这个参数)。Lua也提供了通过使用冒号操作符来隐藏这个参数的声明(通过冒号来声明函数)。先来看下这段代码(这里面还不涉及到类的定义,只是纯粹为了解释self和冒号操作):

    复制代码
    Account = {balance = 0}
    
    function Account.withdraw(obj, v)
        obj.balance = obj.balance + v
    end
    
    a = Account
    a.withdraw(a, 100)
    print(a.balance)
    复制代码

      也可以将函数写成这样(将obj换成self能更好的表达出该参数的语义):

    function Account.withdraw(self, v)
        self.balance = self.balance + v
    end

      self本身是一个Lua的保留字,为了不必给每个函数声明都加上self,Lua提供了冒号操作符:

    function Account:withdraw (v)
        self.balance = self.balance - v
    end

      定义函数时将点改成冒号,函数体内部直接用self参数访问对象自己(self参数表示表自己,也就是对象本身)。同时函数调用也有两种写法,并且这两种写法是通用的:

    a.withdraw(a, 100)
    a:withdraw(100)

      点操作需要显示传入调用者自身。实际上,冒号的效果相当于在函数定义和函数调用的时候,增加一个额外的隐藏参数。这种方式只是提供了一种方便的语法,并没有什么新的内容。我们可以使用dot语法定义函数而用冒号语法调用函数,反之亦然,只要我们正确的处理好额外的 参。

       Lua中的表其实是一个对象。我们定义一个表作为原型(相当于类定义),然后其他的对象(也是一个表)通过metatable和__index机制来访问原型表中的成员。下面看下类的实现:

    复制代码
    Account = {balance = 0}
    
    --将表自己做成metatable,或者也可以在表中加入一个mt成员来作为metatable
    function Account:new(o)
        o = o or {}
        setmetatable(o, self)
        self.__index = self
        return o
    end
    
    function Account:withdraw(v)
        self.balance = self.balance - v
    end
    
    function Account:deposit(v)
        self.balance = self.balance + v
    end
    
    a1 = Account:new{}
    a2 = Account:new{balance = 100}
    a1:withdraw(100)
    print(a1.balance)
    print(a2.balance)
    复制代码

      为了简洁,直接将类本身定为对象表的metatable。上面的new函数还是有一定的技巧性的,之前用过C++/Java等语言的人,应该不太习惯这种写法。因为在强类型语言中,编程者会尽量避免倾向于展示技巧的写法,而使用更简单、可读性更高的写法。但在使用Lua时需要转换一下思维,尤其是在基于Lua基础的东西来扩展一些概念的时候,各种技巧性的东西会很常见,要习惯。

      类继承的实现其实很简单,直接调用基类的new操作,然后在子类中可以添加一些新的成员或方法:

    -- 继承自Account的类(其实就是一个Account对象),SpecialAcount可以重新定义继承来的成员函数,或添加新的成员函数
    SpecialAccount = Account:new()
    
    -- 子类实例化,有趣之处是,s的metatable是SpecialAccount
    s = SpecialAccount:new{limit = 1000}

      可以看到,其实上面的类也是对象,只是看你怎么去理解它,当把一个对象当做模板原型来用时,它就是一个类,但同时,它也是一个对象,很随意有木有。

       多继承:多继承的想法很简单,就是对于子类中不存在的域,在多个父类中依次查找即可。不过此代码有点复杂,如下所示:

    复制代码
    Named = {name = "default"}
    function Named:getname() return self.name end
    function Named:setname(v) self.name = v end
    
    --k为函数名,list为父类列表
    search = function(k, list)
        for i = 1, table.getn(list) do
            local v = list[i][k]        -- 访问第i个父类的k域,存在则返回
            if v then return v end
        end
    end
    
    -- 参数为父类列表
    createClass = function (...)
        c = {}
    
        -- 这里面的k为访问的域
        setmetatable(c, {__index = function(t, k) return search(k, arg)   end})
        
        -- 用作对象的metatable
        c.__index = c
        function c:new(o)
            o = o or {}
            setmetatable(o, c)
            return o
        end
        
        return c
    end
    
    NamedAccount = createClass(Account, Named)
    obj = NamedAccount:new{name = "Paul"}
    print(obj:getname())
    复制代码

      函数search表示在父类列表list中依次查找名字为k的域。

      函数createClass用于创建多继承的类,参数为父类列表。可以看到对象以多继承类为metatable,而多继承类通过metatable在多个父类中依次查找域。

      私有成员的实现:基于闭包来封装掉不想被外部访问的数据或接口。

    复制代码
    function newAccount(initialBalance)
        -- 这里面放的全是私有部分,可以是私有数据或私有函数
        local self = {balance = initialBalance}
        
        local withdraw = function(v) self.balance = self.balance - v end
        local deposit = function(v) self.balance = self.balance + v end
        
        local getBalance = function() return self.balance end
        
        -- 对外公开的接口全在return的表中索引
        return {withdraw = withdraw, deposit = deposit, getBalance = getBalance}
    end
    
    a1 = newAccount(0)
    a2 = newAccount(100)
    a1.withdraw(100)
    print(a1.getBalance())
    print(a2.getBalance())
    复制代码

      一种特殊的“对象”:对象只有一个单一的方法时,可以这样使用。它是前面介绍的oop的一种特例,这种对象不具有继承性,但具有私有性,而且效率比较高:

    复制代码
    function newObject(value)
        return function(action, v)
            if action == "get" then return value
            elseif action == "set" then value = v
            else error("invalid action")
            end
        end
    end
    
    d = newObject(0)
    print(d("get"))
    d("set", 10)
    print(d("get"))
    复制代码

      从上面可以看出这种Single-Method对象实际上就是一个闭包。它不使用表,而是将一个哈桉树看做一个对象。

    http://www.cnblogs.com/sifenkesi/p/3845132.html

  • 相关阅读:
    数据库范式那些事[转]
    C# 之值类型与引用类型参数[基础]
    C# 实体类生成工具
    《浅谈线程池》笔记
    提高网站性能之 —— 减少图片HTTP 请求的方案
    SQL Server 2005 For XML[学习]
    关于数据类型导致的精确计算
    SQL Server 数据库实现之TSQL语句[备忘]
    C# 关键字ref 和out 的详细区别
    关于XML中的名称空间
  • 原文地址:https://www.cnblogs.com/softidea/p/5242966.html
Copyright © 2011-2022 走看看