LoginSignup
6

More than 3 years have passed since last update.

posted at

updated at

Lua オブジェクト指向プログラミング メタテーブルを使用する場合としない場合

Luaはオブジェクト指向プログラミングをサポートしている
現在使用しているオブジェクト指向プログラミングの実装方法をまとめておく

メタテーブルを使用しない場合

クラスの実装とインスタンス化

Class = {}
function Class.new()
    local obj = {}
    obj.func = function(self)
                   --関数の処理
               end;
    return obj
end

local instance = Class.new()
instance:func()

メタテーブルを使用しない場合はnewによって返すテーブルの中に関数を入れてあげます。
継承とオーバーライドは以下の通りです。

継承・オーバーライド

Child = {}
function Child.new()
    local obj = Class.new()
    obj.func = function(self) --オーバーライド
                   --関数の処理
               end;
    return obj
end

returnするテーブルには親クラスのインスタンスを入れてあげます。
この実装の問題点は新たにインスタンスを作成するたびにメソッドを初期化する必要がある点です。

メタテーブルを使用する場合

instance関数

function instance(class, super, ...)
    local self = (super and super.new(...) or {})
    setmetatable(self, {__index = class})
    setmetatable(class, {__index = super})
    return self
end

子クラスの __index に親クラスを、インスタンスの __index に子クラスを設定してあげます。
これによりインスタンス(self)に存在しないキーにアクセスした時、クラスとその親クラスから探してくることができます。

__indexやmetatableについて詳しく知りたい方は、こちらを参考にしてください

クラスの実装

Class = {
    new = function(val1,val2)
        local obj = instance(Class)

        --ここでメンバ変数を宣言
        obj.m_val1 = val1
        obj.m_val2 = val2

        return obj
    end;

    func = function(self)
        -- 処理
    end;
}

クラスの宣言、実装は上記のように行います。

local temp = Class.new(1,2)
temp:func()

new関数を呼んであげればinstance関数によって作成されたインスタンスを返してくれます。
このインスタンスには__indexにクラスとその親クラスが設定してあるため、実装したクラスの関数を呼び出すことができます。

ちなみに

temp.func(temp)
temp:func()

は共に自分自身をメソッドに渡す処理です。下の書き方は上の書き方の省略形ですね。

継承

--親クラス
Parent = {
    new = function(val1,val2)
        local obj = instance(Parent)

        --ここでメンバ変数を実装
        obj.m_val1 = val1
        obj.m_val2 = val2

        return obj
    end;

    parentFunc = function(self)
        -- 処理
    end;
}

--子クラス
Child = {
    new = function(val1,val2)
        local obj = instance(Child,Parent,val1,val2)

        --ここでメンバ変数を実装

        return obj
    end;

    func = function(self)
        Parent.parentFunc(self)  --親クラスのメンバ関数を呼ぶ
    end;
}    

継承は上記のように行います。instance関数の引数に親クラスを与えてあげればよいです。
子クラス内で親クラスの関数を呼びたいときは親クラス.関数名()でアクセスしてください。

オーバーライド

--親クラス
Parent = {
    new = function(val1,val2)
        local obj = instance(Parent)

        --ここでメンバ変数を実装
        obj.m_val1 = val1
        obj.m_val2 = val2

        return obj
    end;

    func = function(self)
        -- 処理
    end;
}

--子クラス
Child = {
    new = function(val1,val2)
        local obj = Parent.new(Child,Parent,val1,val2)
        return obj
    end;

    --オーバーライド
    func = function(self)
        -- 新しい処理
    end;
}    

オーバーライドをしたい時は子クラスに親クラスの関数を新たに定義します。

速度について

kengonakajimaさんのLua OOPの速度比較によると
**metatableを使用する実装では、metatbleを使用しない実装に比べ大幅に速度が落ちるそうです。

追記
まつもとゆきひろ コードの未来では別の実装例が紹介されています。

参考
http://invalid-log.blogspot.com/2015/05/lua.html
https://github.com/kengonakajima/blog/blob/master/articles/luaoo.md

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
6