繼承教學

lua-users home
wiki

本教學說明使用 Lua 執行物件導向繼承的技巧。在繼續之前,建議您先熟悉 物體導向教學元方法教學

簡單類別

以下範例執行沒有繼承的類別

SimpleClass = {}
SimpleClass_mt = { __index = SimpleClass }

-- This function creates a new instance of SimpleClass
--
function SimpleClass:create()
    local new_inst = {}    -- the new instance
    setmetatable( new_inst, SimpleClass_mt ) -- all instances share the same metatable
    return new_inst
end

-- Here are some functions (methods) for SimpleClass:

function SimpleClass:className()
    print( "SimpleClass" )
end

function SimpleClass:doSomething()
    print( "Doing something" )
end

在以上範例中,SimpleClass 代表一個包含所有類別方法的表格,例如類別宣告。SimpleClass_mt 是我們會附加到所建立各類別執行個體的元表格。函式 SimpleClass:create() 會建立一個類別 SimpleClass 的執行個體。建造類別執行個體牽涉到建立一個空白表格,接著將我們的 SimpleClass 元方法附加到它。附加元方法的結果是新的執行個體會尋找我們所附加的元表格,以取得它的客製化行為。

對執行個體的方法呼叫會觸發執行個體上的「索引」事件,導致在執行個體元表格的「__index」成員上進行查找。__index 成員只是一個指向 SimpleClass 的參考。因此,對執行個體的方法呼叫將導致在 SimpleClass 表格中執行查找。

以下是範例

> simple = SimpleClass:create()
> 
> simple:className()
SimpleClass
> 
> simple:doSomething()
Doing something

執行繼承

現在我們要建立新的類別 SubClass,它會繼承函式,並可從 SimpleClass 覆寫(選擇性)。

-- Create a new class that inherits from a base class
--
function inheritsFrom( baseClass )

    -- The following lines are equivalent to the SimpleClass example:

    -- Create the table and metatable representing the class.
    local new_class = {}
    local class_mt = { __index = new_class }

    -- Note that this function uses class_mt as an upvalue, so every instance
    -- of the class will share the same metatable.
    --
    function new_class:create()
        local newinst = {}
        setmetatable( newinst, class_mt )
        return newinst
    end

    -- The following is the key to implementing inheritance:

    -- The __index member of the new class's metatable references the
    -- base class.  This implies that all methods of the base class will
    -- be exposed to the sub-class, and that the sub-class can override
    -- any of these methods.
    --
    if baseClass then
        setmetatable( new_class, { __index = baseClass } )
    end

    return new_class
end

函式 inheritsFrom(baseClass) 採用單一參數,即我們想繼承的類別宣告。函式會傳回一個類別宣告,我們接著可以調整。new_class 是新的類別宣告,待傳回。巢狀函式 new_class:create() 是傳回的類別宣告一部分,且會建立我們正在建立的子類別新執行個體。此函式會建立一個 newinst 表格,且會使用我們的類別表格包含它的方法。新的類別表格接著會在 baseClass 中尋找,如果找不到我們需要的方法,我們就會繼承它的方法。

繼承範例

建構於 SimpleClass,我們現在建立稱為 SubClass,且會從 SimpleClass 繼承並覆寫 className() 的類別

> -- Create a new class that inherits from SimpleClass
> SubClass = inheritsFrom( SimpleClass )
>
> -- override className() function
> function SubClass:className() print( "SubClass" ) end
>
> -- Create an instance of SimpleClass
> simple = SimpleClass:create()
> 
> simple:className()
SimpleClass



> 
> simple:doSomething()
Doing something
> 
> -- Create an instance of SubClass
> sub = SubClass:create()
> 
> sub:className()  -- Call overridden method
SubClass
> 
> sub:doSomething()  -- Call base class method
Doing something
> 

OO 屬性

我們現在可以擴充我們的繼承結構,並新增其他語言中常見的功能,例如存取類別的超級類別,以及提供類型 ID 功能的 isa() 方法

-- A new inheritsFrom() function
--
function inheritsFrom( baseClass )

    local new_class = {}
    local class_mt = { __index = new_class }

    function new_class:create()
        local newinst = {}
        setmetatable( newinst, class_mt )
        return newinst
    end

    if nil ~= baseClass then
        setmetatable( new_class, { __index = baseClass } )
    end

    -- Implementation of additional OO properties starts here --

    -- Return the class object of the instance
    function new_class:class()
        return new_class
    end

    -- Return the super class object of the instance
    function new_class:superClass()
        return baseClass
    end

    -- Return true if the caller is an instance of theClass
    function new_class:isa( theClass )
        local b_isa = false

        local cur_class = new_class

        while ( nil ~= cur_class ) and ( false == b_isa ) do
            if cur_class == theClass then
                b_isa = true
            else
                cur_class = cur_class:superClass()
            end
        end

        return b_isa
    end

    return new_class
end

使用範例

> SimpleClass = inheritsFrom( nil )  -- pass nil because SimpleClass has no super class
> 
> SubClass = inheritsFrom( SimpleClass )
> 
> FinalClass = inheritsFrom( SubClass )
> 
> sub = SubClass:create()
> fc = FinalClass:create()
> 
> print( fc:isa( SubClass ) )
true
> print( fc:isa( FinalClass ) )
true
> print( sub:isa( SubClass ) )
true
> print( sub:isa( FinalClass ) )
false

替代方法:原型基礎

原型基礎的程式設計是一種物件導向式程式設計風格,其中類別並不存在,以及行為再利用(在基於類別的語言中稱為繼承)是透過複製作為原型的現有物件的處理進行。此模式也可以稱為無類別、原型導向或基於執行個體的編寫程式。

[Wikipedia 原型基礎程式設計文章]

大部分的程式碼基本上與上述相同,但已精簡至只有讓「原型基礎程式設計」運作所需的精華。更精確地說,它允許使用複製與原型委派進行原型程式設計。存取物件中未設定的屬性會委派給其原型。此程式碼使用 table 表格作為最基本的原型,而 object 則作為 table 的專門化。函式 object.isa 對原型典範來說並非絕對必要,但更便利。

函式 clone(base_object[, clone_object]) -> table

參數

傳回

如果 new_object 不是 table 類型,則會傳回 new_object(如果不是 nil),否則傳回 base_objectnew_object 的元表格設定為本身,而其 __index 現在指向其原型 base_objectclone 也可用作 object.clone

它也可能會有布林值傳遞為任一引數的問題,因為,呃… 留給讀者練習!? ;-)

函式 isa( clone_object, base_object) -> bool

參數

傳回

如果任一引數都不是 tableisa 會改為傳回類型的比較。它也可用作 object.isa

此函式將在深入的原型階層中表現不佳。

程式碼

function clone( base_object, clone_object )
  if type( base_object ) ~= "table" then
    return clone_object or base_object 
  end
  clone_object = clone_object or {}
  clone_object.__index = base_object
  return setmetatable(clone_object, clone_object)
end

function isa( clone_object, base_object )
  local clone_object_type = type(clone_object)
  local base_object_type = type(base_object)
  if clone_object_type ~= "table" and base_object_type ~= table then
    return clone_object_type == base_object_type
  end
  local index = clone_object.__index
  local _isa = index == base_object
  while not _isa and index ~= nil do
    index = index.__index
    _isa = index == base_object
  end
  return _isa
end

object = clone( table, { clone = clone, isa = isa } )

範例

-- testing "isa"
foo = object:clone()
bar = object:clone()
baz = foo:clone()

print( foo:isa(object) )
print( bar:isa(foo) )
print( baz:isa(foo) )

--[[ output:
true
false
true
]]

--testing prototype delegation

foo = object:clone()
bar = foo:clone()

function foo:speak()
  print(self.thoughts or "foo has no thoughts")
end

bar:speak()

--[[ output:
foo has no thoughts
]]

bar.thoughts = "I may be a clone, but I'm an individual!"
bar:speak()

--[[ output:
I may be a clone, but I'm an individual!
]]


貢獻者: KevinBaca

另請參閱


最近的異動 · 喜好設定
編輯 · 歷程
最後編輯 2011 年 11 月 18 日星期五下午 12:09 GMT (差異)