*Luaリファレンス 要注意点 ~オブジェクト指向~ [#kee16b7e]

**インボカント ~暗黙の第1引数self~ [#ja94c102]

-PerlやPythonでは、明記必須のインボカント。Luaにも存在します。~
インボカントこそ、手続き型言語をオブジェクト指向化する中核概念といえるでしょう。
~
#sh(lua){{
Account = { balance = 0 }
function Account.withdraw (v)
    Account,balance = Account.balance - v
end
}}
これはテーブル内にメソッド的なものを定義した例ですが、これだと
#sh(lua;highlight:[2]){{
a = Account
Account = nil
a.withdraw(10)  ---> エラー
}}
となってしまいます。

-そこで、第1引数とインボカントが活躍します。
#sh(lua){{
function Account.withdraw ( self, v )
    self.balance = self.balance - v
end
}}

-多くの言語では、1番目のself(またはthisとなっている言語も多い)の引数は見えなくなっています。
#sh(lua;highlight:[1]){{
function Account:withdraw( v )    ---> 「.」ではなく、「:」を使うと、1番目の引数にselfを書いたことと同じこととみなされる。
    self.balance = self.balance - v
end
}}

-呼び出しも「:」を使えば第1引数にオブジェクト自体を代入したことと同じとなる。~
通常記法も使えるがその場合はやや間が抜けた記述となるだろう。~
#sh(lua;highlight:[4]){{
a = Account
Account = nil
a.withdraw(a, 10)  --> 通常記法。第1引数にテーブル(オブジェクト)自身を渡す。
a:withdraw(10)     -->  シンタックスシュガー。第1引数にオブジェクト自身を渡していることになる。
}}

**インボカントとインスタンス化 [#zbefa9c6]
-インボカント
#sh(lua;){{
Account = { balance = 0 }

function Account:new (o)  --「:」に注意
    o = o or { }
    setmetatable(o, self)
    self.__index = self
    return o
end

function Account:deposit (v)
    self.balance = self.balance + v
end

a = Account:new{ balance = 0 }
a:deposit(100)
}}
としたとすると、
#sh(lua;){{
Account:new { balance = 0 }
}}
などといったように使用すると、メタテーブルの__indexに対して~
#sh(lua;){{
Account.__index = Account
}}
となる。~
~
そして、a:deposit(100)は a.deposit(a, 100)となるので、~
aに存在しないdepositメソッドは~
getmetatable(a).__index.deposit(a, 100) と検索される。~
~
結果、''Account.deposit(a, 100)'' と指定したことと同じことになる。~
~
即ち、aは自身にメソッドが存在しない時、Account(を一種親的にみなし)呼び出す、
一種、オブジェクト指向の「継承」や「デフォルト値」があるかのような機構を構築出来るということである。

-フィールドの初期値の継承
又、''フィールドの初期値も継承''する。~
例えば、~
#sh(lua;){{
b = Account:new()
print(b.balance) -- b.balance は b自体には存在しないので、Account.balance を見ることとなる。
}}
一方、これにたいして、
#sh(lua;){{
b:deposit(10)
}}
などと呼び出した場合は、Account.deposit(b, 10)が呼び出される形となり、b.blanace = b.balance + 10 されるので、~
この段階で、b自身にbalanceフィールドが形成されることとなる。~

-クラスの継承
#sh(lua;){{
Account = { balance = 0 }

function Account:new (o)
   o = o or { }
   setmetatable(o, self)
   self.__index = self
   return o
end

function Account:deposit(v)
   self.balance = self.balance + v
end

function Account:withdraw(v)
   if v > self.balance then error ("んな金ねーぉ") end
   self.balance = self.balance - v
end
}}
ここで、このAccountを継承するSpecialAccountクラスを作成する
#sh(lua;){{
SpecialAccount = Account:new()  -- この段階ではまだインスタンス
s = SpecialAccount:new{limit = 1000.00}  -- new を実行した時、selfのパラメータがSpecialAccountとなり、self.__indexもSpecialAccountとなった。
}}
これにより、sのメタテーブルはSpecialAccountとなり、sはSpecialAccountのインスタンスであるかのように振舞える。~
~
#sh(lua;){{
s:deposit(100)
}}
を呼び出すと、~
''getmetatable(s).__index.deposit(s, 100)となるので、''~
''ScecialAccount.deposit(s, 100)を実行しようとするが、このdepositも存在しないため、''~
''getmetatable(SpecialAccount).__index.deposit(s, 100)となり、''~
''結果、''~
''Account.deposit(s, 100)が実行される。''~
~
''即ち、インスタンス、子クラス、親クラスの、メソッド探査(階層チェーンの伝播)が構築できたこととなるのである。''

-又、SpecialAccountは独自のメソッドを持ったり、上書き(オーバライド)するのになんら問題はない。
#sh(lua;){{
function SpecialAccount:withdraw (v)   --オーバライド
    if v - self.blance >= self:getLimit() then
        error("引き出しすぎ")
    end
    self.balance = self.balance - v
end

function SpecialAccount:getLimit()
    return self.limit or 0
end
}}
又、プロトタイプ言語であることからわかるように、''いちいちクラスメソッドの単位でオーバーライドせず、インスタンス単位で変えても良い''(もともと区分けがないとも言えるが…)
#sh(lua;){{
function s:getLimit()
    return self.balance * 0.10
end
}}
これで、s:withdrawから呼び出されるgetLimitは、このs:getLimitが呼ばれることとなる。

**多重継承 [#g65ffd69]
-Account クラスと Named クラスを多重継承する。
#sh(lua;){{
Account = { balance = 0 }

function Account:new (o)
    o = o or { }
    setmetatable(o, self)
    self.__index = self
    return o
end

function Account:deposit(v)
    self.balance = self.balance + v
end

----

Name = {}
function Named:getname()
    return self.name
end

function Named:setname(n)
    self.name = n
end

---

local function search (k ,plist)
    for i=1, #plist do
        local v = plist[i][k]
        if v then return v end
    end
end

function MultiInheritance (...)  -- 複数のクラスから
    local c = {}                        -- 新しいクラス
    local parents = {...}

    setmetatable(c, {__index = function(t, k)
        local v = search(k, parents)   -- 該当プロパティを持つ親クラスを特定する
        t[k] = v  -- パフォーマンス改善のためコピーしておく
            return v
    end})

    c.__index = c

    -- 新しいクラスのnewコンストラクタを定義
    function c:new (o)
        o = o or { }
        setmetatable(o, c)
        return o
    end

    return c
end

NamedAccount = MultiInheritance(Account, Named)

account = NamedAccount:new{name="Paul"}
}}
**プライベート [#wc5b1824]
-Perlと同じやり方。~
~
ただ、わかりにくくなるので、LUAではあまりやらない。~
アンダーバーでも付けて、人と運用のレベルでカバーするのがLUAでは普通で~
強制的に文法で対応しようとはしない方が良い。
#sh(lua;){{
function newAccount ( initialBalance)
    local self = {balance = initialbalance}
    
    local deposit = function (v )
                        self.balance = self.balance -v
                    end

    local getBalance = function () return self.balance end
 
    return {
        deposit = deposit
        getBalance = getBalance
    }
end

acc = newAccount(100)
print(acc.getBalance())
}}
**オブジェクトの簡易実装はクロージャで [#raeea973]
>まぁ、他の言語でも全く同じ使用用途。
しかし、複雑化すると、結局クラス化することが多い
#sh(lua;){{
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")) --> 0

d("set", 10)
print(d("get")) --> 10
}}


トップ   一覧 単語検索 最終更新   ヘルプ   最終更新のRSS