2018年6月27日星期三

lua 面向对象的实现问题

一开始打算用最简单的形式,就是如下:
function AIExecuteQueue:New()
    local o = {}
    setmetatable(o, self)
    self.__index = self
    o:init()
    return o
end
一开始没啥问题,但后来逐渐发现问题。
首先我希望要有一个基类Base
Base:New里面绑定了一些事件。
AIExecuteQueue继承自事件
我一开始这么写:
AIExecuteQueue = Base:New()
然后调用AIExecuteQueue:New()
由于o的元表是AIExecuteQueue, 而AIExecuteQueue的元表是Base,看上去还不错,但实际并非如此。
还没有调用AIExecuteQueue:New的时候,Base:New已经被调用了,也就是说事件已经被绑定了,这和我们的预期完全不同。
后来看了云风的实现,发现是这样:
local _class={}
 
function class(super)
 local class_type={}
 class_type.ctor=false
 class_type.super=super
 class_type.new=function(...) 
   local obj={}
   do
    local create
    create = function(c,...)
     if c.super then
      create(c.super,...)
     end
     if c.ctor then
      c.ctor(obj,...)
     end
    end
 
    create(class_type,...)
   end
   setmetatable(obj,{ __index=_class[class_type] })
   return obj
  end
 local vtbl={}
 _class[class_type]=vtbl
 
 setmetatable(class_type,{__newindex=
  function(t,k,v)
   vtbl[k]=v
  end
 })
 
 if super then
  setmetatable(vtbl,{__index=
   function(t,k)
    local ret=_class[super][k]
    vtbl[k]=ret
    return ret
   end
  })
 end
 
 return class_type
end
非常有意思的方法,new本身其实就是构造了一个空table, 这个空table,访问这个table就是访问_class里面的实际table.
那这样怎么继承呢,如果指定了super,那么在访问实际表的时候,首先访问vtbl,如果vtbl里面没有,那么就会访问vtbl的元表,也就是super里面的key,同时还会把这个值拷贝到vtbl中,加快访问速度。
这样反过来看我们的一开始的设计问题,在定义New的时候,生成空tabel a, 让a的元表指向AIExecuteQueue. 然后由于AIExecuteQueue继承自base 我们需要让AIExecuteQueue的元表指向base
function AIExecuteQueue:New()
    local o = {}
    setmetatable(o, self)
    self.__index = self
    
setmetatable(AIEcecuteQueue, Base)
AIExecuteQueue.__index = Base
return oend

最后 我们试想一下怎么重写一个init函数 里面调用base.init

self.super.init(self,xxx)
虽然蛋疼 但暂时也没有更好的办法了

最近发现一个新的问题,self.super不支持递归调用,就是C继承B,继承A,那么c.usper.init()并不会调用A.init()
于是修改如下:
---
--- Created by yxriyin.
--- DateTime: 2017/12/28 16:05
---

local _class={}

function CallBaseFunc() end

function Class(super, o)
    local class_type={}
    class_type.ctor=false
    class_type.super=super
    class_type.New=function(...)
        local obj={}
        setmetatable(obj,{ __index=_class[class_type] })
        do
            local create
            create = function(c,...)
                if c.super then
                    create(c.super,...)
                end
                if c.ctor then
                    c.ctor(obj,...)
                end
            end

            create(class_type,...)
        end

        return obj
    end
    local vtbl= o or {}
    vtbl.super = super
    _class[class_type]=vtbl

    setmetatable(class_type,{__newindex=
    function(t,k,v)
        vtbl[k]=v
    end,
        __index = vtbl,
    })

    if super then
        setmetatable(vtbl,{__index=
        function(t,k)
            local ret=_class[super][k]
            vtbl[k]=ret
            return ret
        end
        })

        function class_type.callBase(self, f, ...)
            if not self.virtualList then
                self.virtualList = {}
            end
            local base = self.super

            if self.virtualList[f] then
                base = self.virtualList[f]
            else
                local base1 = self
                local base2 = base
                while base1 and base2 do
                    if base1[f] == base2[f] then
                        base = base.super
                        base2 = base2.super
                        base1 = base1.super
                    else
                        break
                    end
                end
            end


            self.virtualList[f] = base.super
            local result = base[f](self, ...)
            self.virtualList[f] = nil
            return result
        end
    end



    return class_type
end
这里模拟了一个最简单的虚表的实现
这样子就是self:callBase("method")来实现调用子类的函数,而且支持递归调用

没有评论:

发表评论