【Lua】対象解析

6546 ワード

前に書く
lua自体が継承をサポートしていないことを知っています.luaのすべてのオブジェクトはtableで構成されています.ここでは、メタテーブルを使用してクラスの継承をシミュレートできることを知っています.コアインプリメンテーションは_indexメタメソッド.簡単な実装は次のとおりです.
Super={}    --  
SubClass={}    --  
setmetatable(SubClass,{__index=Super})    --    

単純パッケージ
まず、簡単なパッケージ継承方法を見てみましょう.
BaseObject = {}
BaseObject .__index = BaseObject 

function BaseObject:New(object,...)
  object = object or {}
  setmetatable(object, self)
  self.__index = self
  if object.Ctor then
    object:Ctor(...)
  end
  return object
end

ここでは簡単なベースクラスを実現し,値を伝達できる簡単な構造方法を実現した.しかし、個人的にはこの構造方法を使うのはよくないと思います.むしろ、直接方法を書いて外に初期化方法を書いたほうがいいと思います.
middleclassパッケージ
これはgithubの上で誰かが与えたlua継承の方案で、特徴は簡単で実用的です.githubアドレス、使用するバージョンは4.1です.
簡単な使用例:
local class = require 'middleclass'

Person = class('Person') --    Person 
function Person:initialize(name)
  self.name = name
end
function Person:speak()
  print('Hi, I am ' .. self.name ..'.')
end

AgedPerson = class('AgedPerson', Person) --        Person AgedPerson 
AgedPerson.static.ADULT_AGE = 18 --      
function AgedPerson:initialize(name, age)
  Person.initialize(self, name) --          
  self.age = age
end
function AgedPerson:speak()
  Person.speak(self) --    "Hi, I am xx."
  if(self.age < AgedPerson.ADULT_AGE) then --         
    print('I am underaged.')
  else
    print('I am an adult.')
  end
end

local p1 = AgedPerson:new('Billy the Kid', 13) --      
local p2 = AgedPerson:new('Luke Skywalker', 21)
p1:speak()
p2:speak()

出力:
Hi, I'm Billy the Kid.
I am underaged.
Hi, I'm Luke Skywalker.
I am an adult.

これは公式に与えられた例です.とても簡単で分かりやすいです.公式のチュートリアルもあります
解析:
私たちはclass('Person')という文から解析を始めました.ちょうど彼の仕事の流れを見ることができます.
ここで呼び出された_callメタメソッド、メタメソッドで呼び出されたmiddleclass.class(...),クラスのテーブルを生成します.
setmetatable(middleclass, { __call = function(_, ...) return middleclass.class(...) end })

middleclass.class(...)で呼び出された継承されたクラスsuper:subcalssと個別のクラスを生成する_includeMixin(_createClass(name), DefaultMixin),
_createClass(name)この方法は、クラスの基本構造を作成するために使用されるか、抽象クラスとして理解できる.
local aClass = { name = name, super = super, static = {},
               __instanceDict = dict, __declaredMethods = {},
               subclasses = setmetatable({}, {__mode='k'})  }

属性は次のとおりです.
  • nameクラス名
  • super親のtable
  • static静的テーブル
  • __instanceDictはクラス定義の属性と関数(親を含む)
  • を格納します.
  • __decaredMethod(親を除く現在のクラス宣言方法)
  • subclassesサブクラステーブル、弱参照
  • を使用
    まずaClassを設定します.staticテーブルのメタテーブルは_index = __instanceDict(またはsuper.static)を設定し、aClassメタテーブル_を設定します.indexはstaticおよび_tostringと_callメタメソッド.ここの__callメタメソッドの場合に呼び出されるクラスのnewメソッド.
      if super then
        setmetatable(aClass.static, { __index = function(_,k) return rawget(dict,k) or super.static[k] end })
      else
        setmetatable(aClass.static, { __index = function(_,k) return rawget(dict,k) end })
      end
    
      setmetatable(aClass, { __index = aClass.static, __tostring = _tostring,__call = _call, __newindex = _declareInstanceMethod })
    

    使用_includeMixinこの方法は私たちのクラスに具体的な充填をします.ここではDefaultMixinのテーブルを使用します.このテーブルは、上に構築した抽象クラスを実現するために理解することができます.DefaultMixinでは、次の方法があります.
  • __tostring:デフォルトtostringメソッド
  • initialize:初期化方法はデフォルトのコンストラクション関数
  • に似ています.
  • isInstanceOf:クラスのインスタンス静的メソッドであるかどうかを判断する
  • allocate:インスタンステーブルaを作成する.class属性を追加し、クラステーブルb.クラステーブルの__を指すinstanceDictはメタテーブル
  • に設定されている
    allocate = function(self)
      assert(type(self) == 'table', "Make sure that you are using 'Class:allocate' instead of 'Class.allocate'")
      return setmetatable({ class = self }, self.__instanceDict)
    end,
    

    ●new:allocateメソッドとinitializeメソッドを呼び出し、インスタンスがオブジェクトを出て構築メソッドを呼び出します.●subclass:サブクラスの作成
    subclass = function(self, name)
      assert(type(self) == 'table', "Make sure that you are using 'Class:subclass' instead of 'Class.subclass'")
      assert(type(name) == "string", "You must provide a name(string) for your class")
    
      local subclass = _createClass(name, self)//          。
    
      //                 _propagateInstanceMethod  。
      for methodName, f in pairs(self.__instanceDict) do
        _propagateInstanceMethod(subclass, methodName, f)
      end
    
      //           。
      subclass.initialize = function(instance, ...) return self.initialize(instance, ...) end
      
      //    subclass   ,       
      self.subclasses[subclass] = true
      //     subclassed  ,     
      self:subclassed(subclass)
    
      return subclass
    end
    

    ●isSubclassOf:指定されたクラスのサブクラスかどうか.●include:このクラスにパラメータを結合して呼び出した_includeMixinメソッド.
    _includeMixin:このメソッドを使用して、埋め込み前に構築された抽象クラスを、静的および非静的のメソッドとパラメータをそれぞれ抽象クラスに割り当てます.
    _declareInstanceMethod:クラスに宣言メソッドと属性を追加する
    local function _declareInstanceMethod(aClass, name, f)
      aClass.__declaredMethods[name] = f
    
      if f == nil and aClass.super then
        f = aClass.super.__instanceDict[name]
      end
    
      _propagateInstanceMethod(aClass, name, f)
    end
    
  • への登録先_declaredMethods
  • fがnilの場合、親に行ってフィールド
  • を取得する
  • ドメインをサブクラスに追加
  • _propagateInstanceMethod:ドメインをテーブルに追加し、継承に相当するすべてのサブクラスに追加
    local function _propagateInstanceMethod(aClass, name, f)
      f = name == "__index" and _createIndexWrapper(aClass, f) or f
      aClass.__instanceDict[name] = f
    
      for subclass in pairs(aClass.subclasses) do
        if rawget(subclass.__declaredMethods, name) == nil then
          _propagateInstanceMethod(subclass, name, f)
        end
      end
    end
    
  • name=_の場合index、呼び出し_createIndexWrapper
  • aClass._にfを追加instanceDict[name]中
  • すべてのサブクラスを巡回し、サブクラスにメソッドが含まれていない場合は、サブクラスに追加する(含まれている場合は書き換えに相当する)
  • .
    _createIndexWrapper:はい_index処理
    まとめ
  • __instanceDictは、現在のクラス、およびすべての親定義のインスタンスメソッド(プロパティ),を記録します.indexは自分を指す
  • __declaredMethods現在のクラス宣言を記録する方法(属性)
  • subclasses現在のクラスのすべてのサブクラス、弱参照
  • static静的テーブル、new、include、isSubclassOfなどのメソッドを定義します.index指向_instanceDict. インスタンス変数はstaticテーブルに直接アクセスできません.通過する必要があります.class.staticアクセス.クラスの静的メソッドはclass.static:func
  • を定義する
  • クラスのデフォルトテーブル定義_instanceDict,__declaredMethods,staticなどのプロパティ._indexはstaticテーブルを指し、staticのフィールド(A:new()を直接使用できます._新Indexは_declareInstanceMethodメソッド、フィールドを追加すると更新されます_instanceDictおよび_declareMethods
  • インスタンステーブルはclassプロパティを定義し、現在のクラスを指します.metatableは対応クラスの_instanceDict