LUAオブジェクト向けプログラミング(四)多重継承

7124 ワード


Luaで多重継承を実現するには,多様なスキームがあり,pilではlua著者らが多様な継承を実現するスキームを提供しているが,個人的には,このスキームは簡潔であるが優雅ではないと考えている.その後、雲風(江湖伝説の弊害のある人)はもう一つの簡潔で優雅な解決策を提供し、その実現案はooの思想を深く把握した.しかし、風雲の実現は多重継承の場合は考慮されておらず、ここでは、風雲の実現案に基づいて拡張され、多様な継承の場合をサポートしている.
 
まず風雲の実現を見てみましょう.
 
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

 
上記の関数classを使用すると、luaでクラスを簡単に定義できます.
 
base_type=class() --        base_type function base_type:ctor(x) --    base_type       print("base_type ctor") self.x=x end function base_type:print_x() --          base_type:print_x print(self.x) end function base_type:hello() --           base_type:hello print("hello base_type") end 

 
 
次の方法で、単一の継承を実現できます.
child=class(base_type)--クラスchildがbase_に継承されることを定義します.type
 
function child:ctor() --    child       print("child ctor") end function child:hello() --    base_type:hello   child:hello print("hello child") end 

 
 
childクラスのインスタンスを作成します.
obj = child.new(1) --     ,base_type ctor   child ctor 。           。 obj:print_x() --    1 ,      base_type       。 obj:hello() --    hello child ,        。 

 
 
多層単一継承を実現するには、クラスchild 2を作成し続け、childに継承することで、3層単一継承を実現することができます.ここでclass関数は1パラメータ以下の場合のみ受信し,上のclass関数で多重継承を実現しようとすると実現できないが,それに基づいて修正して実現できる.
 
主な変更点は次のとおりです.
>classのパラメータを可変パラメータに変更すると、任意のパラメータを受信できます.
>子クラスのすべての親を再帰的に巡回し、対応するアクションを実行する必要があります.
 
変更後のclassコードは次のとおりです.
 
local _class={}   function class(...) local cls = {}
cls.ctor=false--コンストラクション関数cls._が存在するかどうかbase = {...}--現在のクラスのベースクラスは、1つ、複数、clsがない可能性があります.new = function(...)      local obj = {}     do         local create         create = function(c,...)             if c.__base then                 for _, k in pairs(c.__base) do                     create(k, ...)                 end
            end             if c.ctor then                 c.ctor(obj, ...)             end         end         create(cls,...)     end
    setmetatable(obj, { __index=_class[cls] })     return obj end
local vtbl={} _class[cls]=vtbl   setmetatable(cls, {__newindex=     function(t, k, v)         vtbl[k] = v
    end })   if cls.__base then     setmetatable(vtbl, {__index =         function(tab, key)             for _, base_cls in pairs(cls.__base) do                 local ret = _class[base_cls][key]                      if ret then                          vtbl[key] = ret                          return ret                      end            end                 return nil         end     }) end   return cls end
 
次に検証します.まず、ベースクラスNameとAccountを2つ作成します.
 
Name = class()
function Name:ctor()     print("Name ctor")     self.name = "Smith" end
function Name:getName()     print("Your name: ".. self.name) end
 
Account = class()
function Account:ctor()     print("Account ctor")     self.balce = 1000 end
function Account:deposit(v)     self.balce = self.balce + v end
function Account:getBalance()     print(self.balce) end
function Account:withdraw(v)     if self.balce < v then          print(string.format("your account remains %d, not enough %d", self.balce, v))          return     end     self.balce = self.balce - v end
 
次に、サブクラスSpecialAccountを作成し、上の2つのベースクラスを継承します.
 
SpecialAccount = class(Name, Account) function SpecialAccount:ctor()     print("SpecialAccount ctor") end
 
SpecialAccountクラスのインスタンスを作成し、継承されたベースクラスの関連インタフェースを呼び出します.
 
sc = SpecialAccount.new()
sc:getName() sc:getBalance()
 
出力結果:
Name ctor
Account ctor
SpecialAccount ctor
Your name: Smith
1000
 
SpecialAccountのオブジェクトが正しく構築され、親のメソッドが継承されていることを示します.しかし、上記の実装には、サブクラスの関数で親クラスの同名関数を呼び出すことができないなど、わずかな欠陥が残っており、mywcyflは解決策を示しており、参考にすることができる.もう1つの問題は、多層継承で、サブクラスのnewメソッドにパラメータを渡すと、親クラスのパラメータの初期化にも使用され、それが合理的かどうか分かりません.
 
(全文完了)