zoukankan      html  css  js  c++  java
  • Lua 之面向对象编程

    Lua 之面向对象编程

    Lua并不是为面向对象而设计的一种语言,因此,仅从原生态语法上并不直接支持面向对象编程,但Lua的设计中仍然包含了很多面向对象的思想,理解它们也更有助于理解Lua自身的一些高级机制。

    对象

    Lua中的table就是一种对象,它可以有函数字段。在面向对象(Object Oriented)编程中,对象的方法(method)通常使用self(或this)参数标识对象自身,在Lua中也可以使用冒号(:)实现类似的功能,如下面的例子:

    Account = {balance=0}
    
    function Account:withdraw1(v)
        self.balance = self.balance - v
    end
    
    function Account.withdraw2(self, v)
        self.balance = self.balance - v
    end
    
    Account:withdraw1(100)
    print(Account.balance)
        
    Account.withdraw2(Account, 100)
    print(Account.balance)

    上面的例子中,withdraw1使用了冒号,隐式使用了self参数;而withdraw2使用了点语法,显式使用self参数;

    可以看出,冒号的作用是在一个方法定义中添加一个额外的隐藏参数(self),以及在一个方法调用中添加一个额外的实参。

    在C++、python等这样的OO语言中,使用class的语法来创建object(或instance),即每个对象都是特定类的实例。

    在Lua中没有类的概念,也就是说对象是没有类型的,那么要如何创建多个具有类似行为的对象呢?

    答案是通过元表的__index元方法,将一个table的__index元方法设置为另一个table,那么后者的方法就被前者继承了。

    一个例子:

    Humman = {age=0}
    
    function Humman:SetAge(v) self.age=v end
    function Humman:GetAge() return self.age end
        
    Chinese = {}
    setmetatable(Chinese, {__index = Humman})
    
    Chinese:SetAge(76.3)
    print(Chinese:GetAge())

    上面的例子中,Chinese会在Humman查找它自己不存在的方法。

    甚至可以模仿构造函数的方式来创建对象:

        
    class = {doc='hello'}
    
    function class:new(obj)
        obj = obj or {}
        setmetatable(obj, self)
        self.__index = self
        return obj
    end
    
    local a1 = class:new()
    print(a1.doc)

    这里名为class的这个table就充当了类的作用,通过调用它的new方法,就能返回一个跟它具有类似行为的对象,正如例子中所示,a1对象继承了class的所有字段。

    继承

    在OO编程思想中,另外一个重要的概念就是继承与派生,即允许由父类派生子类,除了继承基类的行为,子类还可以定义自己独有的行为。

    Lua中实现这种特性,仍然是利用了__index元方法的性质——仅在自身没有索引字段时,才会使用__index元方法的返回结果。

    一个例子:

    Humman = {age=0}
    
    function Humman:SetAge(v) self.age=v end
    function Humman:GetAge() return self.age end
    
    function Humman:new(obj)
        obj = obj or {}
        setmetatable(obj, self)
        self.__index = self
        return obj
    end
    
    Chinese = Humman:new()
    
    function Chinese:SetAge(v) self.age = v*0.9 end
    
    Beijing = Chinese:new{city='bj'}
    
    Beijing:SetAge(70) 
    print(Beijing:GetAge()) -- 63

    例子中,Chinese继承自Humman,但它重新定义了SetAge方法,而在Beijing这个对象中调用SetAge时,首先查找Chinese,找不到才继续查找 Humman,所以这里,时Chinese的SetAge方法生效了。

    上面提供的继承实现思路是将派生类的__index元方法置为代表基类的table,如果要实现多继承,这种方法就不可行了。多继承要求把__index实现为一个函数,它需要遍历所有基类的字段。

    一个例子:

    parent1 = {a="hello"}
    parent2 = {b="world"}
    
    local function search(field, parents)
        for i=1, #parents do
            local v = parents[i][field]
            if v then return v end
        end
    end
    
    function createClass(...)
    
        local c = {}
        local parents = {...}
    
        setmetatable(c, {__index=function(t, field) return search(field, parents) end} )
        c.__index = c
    
        function c:new(o)
            o = o or {}
            setmetatable(o, c)
            return o
        end
    
        return c
    end
    
    c = createClass(parent1, parent2)
    print(c.a)  -- hello
    print(c.b)  -- world

    访问权限

    我们知道C++的类中,每个成员都有一个访问标签:public、private、protected,通过访问标签来限定对象中每个成员是否对外可见。而通常的做法是将数据成员标记为private,将函数成员标记为public。

    前面介绍过Lua的对象是通过table实现的,而table的每个字段都可以直接通过索引访问,因此lua并不打算直接提供对其内部字段访问权限的控制机制。但如果我们一定要让lua对象也实现访问控制,也是有办法的。

    一个例子:

    function Humman(initialAge)
    
        local self = {age=initialAge}
    
        local SetAge = function(v) self.age=v end
        local GetAge = function() return self.age end
    
        return {GetAge=GetAge, SetAge=SetAge}
    
    end
    
    Chinese = Humman(73)
    print(Chinese.GetAge()) -- 73
    
    Chinese.SetAge(80)
    print(Chinese.GetAge()) -- 80

    上例中,Humman是一个工厂函数,使用它创建了一个Chinese对象。在Humman内部,将类的状态(需要保护的类成员)赋给了一个局部变量,并将类的方法(即类的对外接口)作为一个table返回给了Chinese对象,之后Chinese对象只能通过返回的方法间接操作Humman类的状态,而无法直接访问到它们。

    单一方法(single-method)对象

     当对象只有一个方法时,将这个单独的方法作为对象返回。比如,一个具有状态的迭代器就是单一方法对象。

    单一方法对象还有一种情况,它根据某个参数来完成不同的操作,实现一个类似调度器的功能,例如:

    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"))
                     

    上面的例子中,返回的单一方法对象用一个closure。

  • 相关阅读:
    js 和 jquery 动态创建元素
    京东火车票正式上线:开卖火车票!
    网易旗下新域名泰坦尼克曝光:要出航海题材新作
    Facebook CEO扎克伯格造访日本,获首相会见
    struts2教程系列
    hadoop方面的资料
    FlexViewer入门资料
    深入浅出Flex Viewer系列
    flex css
    【Flex4中的皮肤使用组件数据】系列
  • 原文地址:https://www.cnblogs.com/chenny7/p/4050188.html
Copyright © 2011-2022 走看看