• Lua的面向对象——多重继承、私密性


    本文转载于:http://www.benmutou.com/archives/1800

    在Lua中的多重继承和私密性可能用得比较少,也可能只是我个人用得比较少。

    本来想偷懒不写这文章的,因为我今天刚买了个漂移板,连起步都还没学会啊,想多学一会。

    咳咳,本着坚持不懈、负责到底的态度,我还是决定随便写几句~(小若:随便写几句是几吨意思啊?!)

    1.多重继承之在多个类中查找一个字段

    我发现这些高(shen)智(jing)商(bing)人群真的很厉害,这种技巧都能想到,很佩服。

    其实多重继承没什么特别的,除非两个将要被继承的类有相同的函数名和属性,否则,处理起来很简单。

    无非就是在多个table中查找某个字段而已,不简单吗?Lua里的继承就是在别人的table里查找自己不存在的字段罢了。

    那么,单继承与多重继承的差别也在这里,一个是只查找一个table,另一个是查找两个或以上的table。

    我们就先来看看如何从2个或多个table中查找某个字段,如下代码:

    function search(classes, key)
        for i = 1, #classes do
            local value = classes[i][key];
            if value ~= nil then
                return value;
            end
        end
    end
    local t1 = {name = "hehe"};
    local t2 = {game = "who"};
    print(search({t1, t2}, "game"));

    这里的classes参数,是一个table,这个table里又存放了多个table,也就是我们想要继承的那些类。
    而key就是要查找的字段。

    只需要遍历所有的table,判断这个字段是否在某个table里,找到之后,就返回这个值。

    我们的测试代码就是从t1、t2中查找game这个字段,t1、t1可以看成是两个类。

    输出结果如下:

    [LUA-print] who

    2.多重继承之创建继承多个类的子类

    刚刚的search函数很简单吧?别急着开心,那只是预热一下而已,真正创建多重继承的函数比较复杂。

    如下代码:

    function createClass(...)
        local parents = {...};
        local child = {};
       
        -- 设置类的元表
        setmetatable(child, {
            __index = function(table, key)
                return search(parents, key);
            end
        })
       
        -- 给类新增一个new函数,用于创建对象
        function child:new()
            o = {};
            setmetatable(o, child);
            child.__index = child;
            return o;
        end
       
        -- 返回这个继承了多个类的子类
        return child;
    end

    createClass函数就是用来创建一个继承了多个类的子类,有点小复杂,慢慢分析:

    1) 参数是一个可变参数,我们要将多个被继承的类作为参数传递进来

    2) parents用于保存这些被继承的类

    3) 创建一个新的table——child,它就是我们想要的那个继承了多个类的子类

    4) 给child设置元表,并且设置__index元方法,__index元方法可以是一个函数,当它是一个函数时,它的参数就是元表所属的table,以及要查找的字段名。

    5) 我们在__index元方法函数里调用search函数,从多个父类中查找所需的字段。于是,当调用child的某个函数时,就会从各个父类中查找,这已经完成了继承的工作了。

    6) 接下来就是我们所熟悉的new函数,用来创建child的子类,实现方式和上一篇所说的是一样 ,如果你忘记了,可以看看这篇文章:http://www.benmutou.com/archives/1791

    7) 最后返回child,一切都完成了。

    看似很复杂,其实还是对__index的应用而已。

    我们赶紧来测试一下吧,如下代码:

    -- 一个精灵类
        TSprite = {}
        function TSprite:hello()
            print("谁跟你hello!");
        end
       
        function TSprite:new()
            o = {}
            setmetatable(o, self);
            self.__index = self;
            return o;
        end
       
        -- 一个子弹类
        TBullet = {}
        function TBullet:fire()
            print("别动,再动我就瞄不准了!");
        end
        function TBullet:new()
            o = {}
            setmetatable(o, self);
            self.__index = self;
            return o;
        end
       
        -- 继承了两个类的子类
        local BulletSprite = createClass(TSprite, TBullet);
       
        -- 子类的对象
        local bSprite = BulletSprite:new();
        bSprite:hello();
        bSprite:fire();

    这里创建了两个类:TSprite和TBullet。

    然后调用createClass函数,创建一个继承了TSprite和TBullet的子类。

    最后创建子类的对象,调用对象的hello和fire函数。

    输出结果如下:

    [LUA-print] 谁跟你hello!
    [LUA-print] 别动,再动我就瞄不准了!

    3.类的私密性

    这里来说一个和多重继承无关的技巧,那就是私密性。

    对于Java、C++等语言,我们都很熟悉,public、private、protected等关键词。

    这些关键词让封装成为了可能。

    然后,Lua里是没有私密这种说法的,类也是一个table,table的所有字段都是可以调用的,并没有说哪些是公有的,哪些是私有的。

    如果有某些函数和属性不希望被外部调用,那么,也可以,不过这种实现方式看起来很别扭:

    function createTSprite()
        local self = {name = "benmutou"};
        local function myBus()
            print("myBus是我自己的函数,你不能直接调用");
        end
        local function myGame()
            print("myGame是我自己的函数,你不能直接调用");
        end
        local function hello()
            print("hello:");
            myBus();
        end
        local function hi()
            print("hi:");
            myGame();
        end
       
        local function setName(newName)
            self.name = newName;
        end
       
        return {hello = hello, hi = hi, setName = setName};
    end

    我们已经不需要用到冒号来定义函数了,这个类的name、myBus、myGame都是不希望给外部直接调用的。

    调用createTSprite函数后,会返回一个新的table,这个table仅仅存放了一些字段,这些字段就是能够被外部直接调用的函数或者是属性。

    来看看测试代码:

     local sp = createTSprite();
        sp.hello();
        sp.hi();

    输出结果如下:

    [LUA-print] hello:
    [LUA-print] myBus是我自己的函数,你不能直接调用
    [LUA-print] hi:
    [LUA-print] myGame是我自己的函数,你不能直接调用

    这样,我们创建的对象就只能使用hello、hi、setName函数。

    而其他的name、myBus、myGame只能通过这几个能使用的函数去调用,而不能直接调用。

    这样就能完成私密性了。

    不过,我个人有点迷糊,因为这已经不太像一个类的样子了。

    4.结束

    好了,关于面向对象的内容,暂时介绍到这里。

    可能介绍的都比较基础,目的是为了巩固Lua基础。

  • 相关阅读:
    异常处理(五)
    抽象与接口(七)
    MYSQL创建数据库时候直接指定编码和排序规则
    C#基本语法(一)
    C#中字符串处理(三)
    Ajax实例化(五)
    C#函数(二)
    MemberCache学习摘要
    ORACLE 根据日期范围自动日期table
    动态执行SQL语句,并输出参数
  • 原文地址:https://www.cnblogs.com/guangyun/p/4685543.html
Copyright © 2020-2023  润新知