Dear Rob,
Looks like we have an OOP.lua module is not compatible with the new 2016.2866 build
Could you give us a hint of how can we solve the problem. (How we should modify the code)
Here is a part of the code sample:
--## package path --[[packagePath("lib.OOP")]]-- --## package requirements --## class:OOP --OOP是一個物件導向類別。協助lua語言更接近物件導向。提供多種方法實現類別、封裝、繼承與多態,允許函數限定參數型別,增加程式撰寫的嚴謹程度,提升程式碼重用性,使程式更容易被擴充與除錯。 --此類別無實例,所以沒有new()方法(建構函數)。此類別不會參與繼承,本身也無法被存取(只會傳回nil)。 -- --\<公開屬性與方法\>:此類別的所有公開方法與屬性都被設定成全域方法,可在任何地方直接呼叫,但無法透過此類別呼叫。OOP類別會在它被require()引入時立即進行佈署,所有透過OOP設置的全域方法與屬性都將立即可用。 --由OOP所建立的方法與屬性等同於關鍵字,他們的名稱將成為保留字元,他們無法被修改或更動。必須注意的是,大部分作為關鍵字的方法與屬性,只能被用在類別或物件的定義區塊當中。 -- --\<定義類別與物件\>:透過呼叫class()來開始一個類別定義區塊,呼叫endClass()則可結束類別定義區塊。在區塊當中可使用 set, get, func, override, const 等關鍵字來定義函數和屬性。 --通常一個類別會有一個new()方法。用來建立這個類別的實例。在new()方法中,必須使用object() 和 endObject()方法所圍成的區塊來定義物件。在這範圍中同樣可使用上述的關鍵字來定義函數與屬性。 -- --\<虛擬類別\>:coronaSDK 的官方API 並非物件導向架構。換而言之透過官方API所產生的物件(例如:display.newImage())並沒有所屬的類別。這表示我們將無類別可繼承,而當這些物件被當作參數傳入時,我們也很難確認他的型別。 --透過虛擬類別的機制,我們將可以解決這個問題。透過 virtualClass()與virtualObject()方法,我們將官方API用來產生物件的函數(例如:display.newImage()),轉換成虛擬類別來使用。必要時虛擬類別也可以繼承類別,但僅限繼承抽象類別。 -- --●警告:這個文件當中的程式碼,具有相當複雜的邏輯。並且會影響整個 library的正常運作。即使除錯時錯誤訊息指向此處,卻經常是由於其他文件中,有語法以不正確的方式呼叫此文件中的方法,或傳入不正確的參數。除非「非常確定」問題發生於此文件,否則請盡可能的不要改動此文件的內容。 -- 如果要改動此文件的內容,請確保你的頭腦是清楚的,而且你知道你在做什麼。否則請詢問作者 Chris (cocoychris@gmail.com)。 --[[local OOP=abstract(class("OOP")) local OOP=object(OOP) return endObject()]]-- local OOP=nil--不會傳回任何值 --## static private property local gProtectProps={}--這個table用來儲存全域屬性當中透過proxy轉址而訪問的受保護屬性(和方法),它們是唯讀的。由於是全域屬性,所以可以在任何地方讀取或呼叫。 local newObj--正在被文件定義屬性或方法的物件 local newClass--正在被文件定義屬性或方法的類別 local specialPropAccessKey={}--以這個鑰匙當作索引來存取物件特殊屬性,特殊屬性由OOP物件設置,並只能由OOP物件讀取。 local newObjList={}--此清單用來儲存巢狀object()endObject()結構中,當前階層以下階層的newObj參考。 local newFuncName=""--透過func關鍵字建立新函數時,函數名稱會被暫存於於此處。 local virtualClassDict={}--所有透過requireVirtual()建立的虛擬類別,都會被集中於此。 local oldNativeFuncDict={}--所有被複寫的全域官方API原始方法都會被放在這裡 --## static public property ----全域關鍵字---- gProtectProps.override={}--複寫方法的關鍵字。用以在物件的定義中,複寫物件所繼承的方法。 gProtectProps.set={}--建立setter的關鍵字。在物件或類別定義中,透過此關鍵字指定的方法,將被當作一個可被 寫入 的屬性使用。指定的方法必須要設定一個參數,以便接收傳入的值。 gProtectProps.get={}--建立getter的關鍵字。在物件或類別定義中,透過此關鍵字指定的方法,將被當作一個可被 讀取 的屬性使用。若僅指定getter而沒有setter,那麼該屬性便是唯讀的。指定的方法必須有一個傳回值。 gProtectProps.const={}--建立constant的關鍵字。在物件或類別定義中,透過此關鍵字指定的屬性將成為唯讀屬性,其值將無法更改。 gProtectProps.func={}--建立function的關鍵字。不同於預設的function 關鍵字,透過此關鍵字宣告的函數將支援資料型別檢查,呼叫該函數時若參數或傳回值型別錯誤,將會出現錯誤訊息。 ----全域類別---- --gProtectProps.Class={}--虛擬類別「Class」。所有類別作為物件,都屬於「Class」這個類別的實例。 --gProtectProps.Object={}--虛擬類別「Object」。所有類別都繼承自這個類別。 gProtectProps.Class=require("lib.Class")--虛擬類別「Class」。所有類別作為物件,都屬於「Class」這個類別的實例。 gProtectProps.Object=require("lib.Object")--虛擬類別「Object」。所有類別都繼承自這個類別。 ----全域資料型別---- gProtectProps.Number="number"--參數限定型別 Number gProtectProps.String="string"--參數限定型別 String gProtectProps.Boolean="boolean"--參數限定型別 Boolean gProtectProps.Nil="nil"--不建議使用,請改用Any表示任意型別 gProtectProps.Function="function"--參數限定型別 Function gProtectProps.Table="table"--參數限定型別 Table gProtectProps.Thread="thread"--參數限定型別 Thread gProtectProps.UserData="userdata"--參數限定型別 UserData gProtectProps.Any="any"--特殊值,表示可接受任何型別 gProtectProps.Void="void"--特殊值,表示不該有任何參數傳回,此型別尚未被實作,請勿使用。 --## static private method --設置虛擬類別-- local function createVirtualClass() gProtectProps.Class:init(gProtectProps,specialPropAccessKey,oldNativeFuncDict) gProtectProps.Object:init(gProtectProps,specialPropAccessKey,oldNativeFuncDict) end --取得或設置目前的物件特殊屬性 local function getSpecProps(object) local result if(object) then result=object[specialPropAccessKey] end return result end --函數參數檢驗器 local function funcArgTester(proxyTable,table,...) local argList={...} local typeList=proxyTable.typeList local argValid=true if(#argList\<proxyTable.requireArgCount) then error("ERROR: Incorrect argument count to function '"..proxyTable.funcName.."' (expect: "..proxyTable.requireArgCount..", got: "..#argList..") on "..proxyTable.objNameText.." .") argValid=false elseif(argValid) then for i = 1, #typeList do local expectType=typeList[i] local arg=argList[i] local argType=type(arg) --邏輯:如果 此參數應檢查,且以下兩種狀況之一發生:「1.在必要參數範圍之內 2.參數不為Nil。」且參數與指定類型不相符,則擲回錯誤。 if(expectType~=Nil and expectType~=Any and (i\<=proxyTable.requireArgCount or arg~=nil) and argType~=expectType and not isClass(arg,expectType)) then --轉為類別名稱 expectType=getClassName(expectType) or expectType or "(undefined)" argType=getObjClassName(arg) or argType or "(undefined)" error("ERROR: Bad argument #"..i.." to function '"..proxyTable.funcName.."' (expect: "..expectType..", got: "..argType..") on "..proxyTable.objNameText.." .") argValid=false end end end local returnValue if(argValid) then returnValue=proxyTable.func(...) local returnType=proxyTable.returnType if(returnType) then local gotRetType=type(returnValue) if(gotRetType~=returnType and not isClass(returnValue,returnType) and gotRetType~=Nil) then if(isClass(returnType,Class))then returnTypeName=getClassName ( returnType ) else returnTypeName=tostring(returnType) end error("ERROR: Bad return from function '"..proxyTable.funcName.."' (expect: "..returnTypeName..", got: "..tostring(gotRetType)..").") end end end return returnValue end --比較override新版本的型別限定是否相容於舊的限定。 local function compareTypeList(specProps,proxyTable) local valid=true local oldProxyTable=specProps.proxyTableDict[newFuncName] local newTypeList=proxyTable.typeList if(oldProxyTable) then local oldTypeList=oldProxyTable.typeList valid=#oldTypeList\<=#newTypeList--新的參數限定數量可以比舊的多 if(valid) then valid=proxyTable.requireArgCount\<=oldProxyTable.requireArgCount--新的必要參數數量可以比舊的少 end if(valid) then for i = 1, oldProxyTable.requireArgCount do valid=oldTypeList[i]==newTypeList[i]--型別完全相等 if(not valid) then break end end valid=(oldProxyTable.returnValue==nil or oldProxyTable.returnValue==Nil)or oldProxyTable.returnValue==proxyTable.returnValue end assert(valid,"ERROR: Overriding function '"..newFuncName.."' failed. Argument type specification cannot be different from super function.") end return valid end --建立轉址table,將函數與資訊儲存, --每一個透過func 關鍵字建立的函數都有一個ProxyTable。用來記錄相關資訊。 --funcData 是一個陣列,前面的值是函數的參數型態限定,最後一個值是函數的參照。 local function createProxyTable(funcData) local specProps=getSpecProps(newObj) if(specProps) then local proxyTable={} local lastData=table.remove(funcData) if(type(lastData)=="function") then--返回值限定被省略 proxyTable.returnType=nil proxyTable.func=lastData or error("ERROR: Creating function '"..newFuncName.."' on "..newObj.toString().." failed. Got one or more nil value in bracket '{}' of function definition.") else proxyTable.returnType=lastData--寫入返回值限定 lastData=table.remove(funcData) if(lastData and type(lastData)=="function") then proxyTable.func=lastData else error("ERROR: Creating function '"..newFuncName.."' on "..newObj.toString().." failed. Got one or more nil value in bracket '{}' of function definition.") end end if(type(funcData[#funcData])=="number") then--寫入必要參數數量 proxyTable.requireArgCount=table.remove(funcData)--有指定 else proxyTable.requireArgCount=#funcData--無指定,自動以長度作為預設 end proxyTable.typeList=funcData proxyTable.funcName=newFuncName proxyTable.\_\_call=funcArgTester--轉址到參數檢驗器 proxyTable.objNameText=newObj.toString() local valid=compareTypeList(specProps,proxyTable) if(valid) then setmetatable(proxyTable, proxyTable) specProps.proxyTableDict[newFuncName]=proxyTable --建立替代原始函數的介面函數 local function interfaceFunc(...) return proxyTable(...) end --將介面函數寫入物件 -- print("Adding function with keyword 'func'.",newFuncName) specProps.funcDict[newFuncName]=interfaceFunc end end end ---------------------------覆寫原始方法------------------------------ local function redefineFunctions() --覆寫 tostring() oldNativeFuncDict.tostring=\_G.tostring \_G.tostring=function(e) if(getObjClass(e)) then return e:toString() else return oldNativeFuncDict.tostring(e) end end end ---------------------------設置關鍵字 Proxy-------------------------- --\<設置函數複寫proxy\> --一般複寫 local function overrideHandeler(table,key,value) local specProps=getSpecProps(newObj) if(specProps and type(value)=="function") then if(specProps.funcDict[key]) then if(specProps.proxyTableDict[key]==nil) then if(specProps.superProps)then specProps.superProps[key]=specProps.funcDict[key] end specProps.funcDict[key]=value else error("ERROR: Overriding method '"..key.."' failed. Argument type specification required.") end else error("ERROR: Function '"..key.."' does not exist, therefore cannot be overridden.") end else error("ERROR: Overriding method '"..key.."' failed. Illegal 'override' usage.") end end --進階複寫。此種複寫使用方式等同於func關鍵字,允許在複寫時指定資料型別。 local function advancedOverrideHandeler(table,key) local specProps=getSpecProps(newObj) local result=function ()end newFuncName=key if(specProps)then if(specProps.proxyTableDict[newFuncName]~=nil) then result=createProxyTable if(specProps.superProps)then specProps.superProps[newFuncName]=specProps.proxyTableDict[newFuncName] end else error("ERROR: Function '"..newFuncName.."' does not exist, therefore cannot be overridden.") end else error("ERROR: Overriding function '"..newFuncName.."' failed. Keyword 'override' can only be used in class or object definition.") end return result end local function setOverrideProxy() local overrideProxy=gProtectProps.override local metaTable={\_\_index=advancedOverrideHandeler,\_\_newindex=overrideHandeler} setmetatable(overrideProxy, metaTable) end --\<設置setter的proxy\> local function setterHandeler(table,key,value) local specProps=getSpecProps(newObj) if(specProps and type(value)=="function") then--允許寫入 specProps.setFuncDict[key]=value else--寫入失敗 error("ERROR: Adding setter '"..key.."' failed.") end end local function setSetterProxy() local setterProxy=gProtectProps.set local metaTable={\_\_newindex=setterHandeler} setmetatable(setterProxy, metaTable) end --\<設置getter的proxy\> local function getterHandeler(table,key,value) local specProps=getSpecProps(newObj) if(specProps and type(value)=="function") then specProps.getFuncDict[key]=value else error("ERROR: Adding getter '"..key.."' failed.") end end local function setGetterProxy() local getterProxy=gProtectProps.get local metaTable={\_\_newindex=getterHandeler} setmetatable(getterProxy, metaTable) end --\<設置constant的proxy\> local function constantHandeler(table,key,value) local specProps=getSpecProps(newObj) if(specProps) then if(specProps.constDict[key]==nil) then specProps.constDict[key]=value -- print("constant", key,value) else print("WARNING: Attempt to change value of constant '"..key.."'.") end else error("ERROR: Adding constant '"..key.."' failed. Keyword 'const' can only be used in class or object definition.") end end local function setConstantProxy() local constantProxy=gProtectProps.const local metaTable={\_\_newindex=constantHandeler} setmetatable(constantProxy, metaTable) end --設置func的proxy --當func關鍵字被index,傳回函數createProxyTable,以便承接函數定義資料。 local function funcHandeler(table,key) local specProps=getSpecProps(newObj) local result=function ()end newFuncName=key if(specProps)then if(specProps.proxyTableDict[newFuncName]==nil) then result=createProxyTable else error("ERROR: overriding object method '"..newFuncName.."' through key word 'func' failed. Keyword 'override' required.") end else error("ERROR: Adding function '"..newFuncName.."' failed. Keyword 'func' can only be used in class or object definition.") end return result end local function setFuncProxy() local funcProxy=gProtectProps.func local metaTable={\_\_index=funcHandeler,\_\_newindex=function ()end} setmetatable(funcProxy, metaTable) end ----------------設置物件 Proxy----------------- --設置super的proxy,使其屬性成為唯讀 -- local function setSuperProxy(specProps) -- local super={} -- setmetatable(super,{\_\_index=specProps.superFuncDict}) -- return super -- end --為proxy準備 local function prepareObjProxy(obj) obj=obj or newObj specProps=getSpecProps(obj) --將所有setter與getter函數存在這兩處 specProps.funcDict={}--物件的方法將被置於此處保護起來 specProps.setFuncDict={}--物件的setter方法 specProps.getFuncDict={}--物件的getter方法 -- specProps.superFuncDict={}--物件的繼承方法,被複寫(override)時會被轉移到此處保存 --specProps.superSetFuncDict={}--未來可以加入 --specProps.superGetFuncDict={}--未來可以加入 -- specProps.constDict={super=setSuperProxy(specProps)}--物件的常數,包含一個特殊常數super,透過此常數可存取已被複寫的方法。 specProps.constDict={}--物件的常數 specProps.proxyTableDict={}--使用func關鍵字建立的函數,都會有一個proxyTable包含該函數的相關資訊,這些proxyTable被集中在此。 specProps.propExistDict={}--將物件上存在的固定屬性指向True,確保屬性不會因為被設為Nil而被移除。 --將obj上的 toString方法 轉移到特殊屬性當中保護起來。 if(obj.toString) then specProps.funcDict.toString=obj.toString obj.toString=nil--移除原本的toString,以免干擾。 end end --為原廠API物件建立函數呼叫轉址。手動將呼叫物件,設成該物件。 local function createFactoryFuncProxy(funcName,specProps,factoryFunc) --這裡的table是發出呼叫的物件,此物件是我們建立的虛擬物件,為避免將虛擬物件傳遞給原廠物件,我們必須將它手動改成原廠物件。 local function proxyFunc(table,...) return factoryFunc(specProps.factoryObject,...) end specProps.factoryFuncProxyDict[funcName]=proxyFunc --加入清單 return proxyFunc end --啟動物件的proxy local function applyObjProxy() local specProps=getSpecProps(newObj) --轉向到key對應的get方法 local function objIndex(table,key) local value -- print("Index",table,getObjClassName(table),key) if(specProps.getFuncDict[key]) then--getter方法 value=specProps.getFuncDict[key]() elseif(specProps.constDict[key]) then--constant(常數) value=specProps.constDict[key] elseif(specProps.funcDict[key]) then--一般函數 -- print("call func",key,table) value=specProps.funcDict[key] elseif(specProps.factoryObject and specProps.factoryObject[key]) then--在 factoryObject 上找到屬性() value=specProps.factoryObject[key] if(type(value)=="function") then--若為函數,就必須置換成proxy函數。避免將此虛擬物件被當作self參數傳入原廠物件。 value=specProps.factoryFuncProxyDict[key] or createFactoryFuncProxy(key,specProps,value) end -- else--物件上的屬性 -- value=rawget(table, key) end return value end --轉向到key對應的set方法 local function objNewIndex(table,key,value) -- print("NewIndex",table,getObjClassName(table),key,value) local isDefining=table==newObj--物件是在定義過程中被附加屬性 if(specProps.setFuncDict[key]) then--找到setter,轉交給setter函數處理 -- print("found setter.") specProps.setFuncDict[key](nil,value) elseif(specProps.getFuncDict[key] or specProps.constDict[key]) then--未找到setter,但找到getter或constant,此為受保護的屬性! print("WARNING: Attempt to set property '"..key.."' (A constant or a read-only variable).") elseif(specProps.funcDict[key]) then--已找到對應的函數,不允許複寫 print("WARNING: Attempt to override object method '"..key.."' failed. Keyword 'override' required.") elseif(specProps.factoryObject and specProps.factoryObject[key]~=nil) then--在factoryObject找到屬性 if(type(specProps.factoryObject[key])=="function") then--若為函數,則阻止寫入。 print("WARNING: Attempt to override object method '"..key.."' failed. This method is protected and cannot be overridden.(a factory object method.)") else specProps.factoryObject[key]=value--將值寫入屬性(而非在物件上新增屬性) -- print("write value "..value.." to factory object property '"..key.."'.") end elseif(specProps.isDynamic or isDefining or specProps.propExistDict[key]) then--沒有setter和getter,若允許動態新增 或 物件正在被定義 或 在固定屬性清單中找到屬性,則加入新的屬性。 --附加屬性或方法 if(type(value)~="function") then -- print("dynamic property adding.",key,value) specProps.propExistDict[key]=true rawset(table, key, value) else -- print("dynamic function adding.",key) specProps.funcDict[key]=value end else print("WARNING: Adding property '"..key.."' to "..tostring(table).." failed! Dynamic property adding is not supported to this object.") end end --設置 MetaTable if(not getmetatable(newObj)) then local objMetaTable={\_\_index=objIndex,\_\_newindex=objNewIndex} setmetatable(newObj, objMetaTable) end end -----------------------設置全域(\_G) Proxy-------------------- --設置\_G的屬性Proxy --讀取 local function gIndex(table,key) local value=gProtectProps[key] return value or rawget(\_G, key) end --寫入 local function gNewIndex(table,key,value) if(gProtectProps[key])then--防止複寫 print("WARNING: Global property '"..key.."' is read-only.") else rawset(\_G, key, value) end end --設置\_G(全域)的MetaTable以重新導向 local function setGMeta() local gMetaTable={} gMetaTable.\_\_index=gIndex gMetaTable.\_\_newindex=gNewIndex setmetatable(\_G, gMetaTable) end --初始化 local function init() createVirtualClass() setOverrideProxy() setSetterProxy() setGetterProxy() setConstantProxy() setFuncProxy() setGMeta() gProtectProps.packagePath("lib.OOP") redefineFunctions() end --## static public method ----------------------類別定義關鍵字(方法)------------------ --[[--建立新的類別,並開始類別定義區塊 --name:類別名稱 --extendsClass:繼承的類別 func.class{String,Class,1, function(name,extendsClass) end ,Class}]]-- function gProtectProps.class(name,extendsClass) if(newClass==nil)then newClass=object(Class)--類別作為物件時屬於類別class的實例 classSpecProps=getSpecProps(newClass) classSpecProps.name=name classSpecProps.isAbstract=false extendsClass=extendsClass or Object if(extendsClass and isClass(extendsClass,Class)) then --繼承類別 local extInheritDict=getSpecProps(extendsClass).inheritDict classSpecProps.inheritDict={[newClass]=true} for key, value in pairs(extInheritDict) do classSpecProps.inheritDict[key]=value end classSpecProps.superClass=extendsClass -- print("CLASS:",name,"extends",getClassName(extendsClass)) end else error("ERROR: Creating class '"..name.."' failed. Class definitions are nested or 'endClass()' function call is missing!") end return newClass end --[[--建立新物件,並開始物件定義區塊 --class:物件的類別 --superConstructorCall:呼叫所繼承類別的new()方法(建構函數)並取得傳回的實例。抽象類別免填。 func.object{Class,Object,1, function(class, superConstructorCall) end ,Object}]]-- function gProtectProps.object( class, superConstructorCall) --處理抽象類別,superConstructorCall 由抽象類別自行呼叫並填入。 local superClass=getSpecProps(class).superClass if(superClass and superClass~=Object) then local superSpecProp=getSpecProps(superClass) if(superSpecProp.isAbstract and superSpecProp.abstractNew) then superConstructorCall=superSpecProp:abstractNew()--呼叫抽象類別的 superConstructorCall end end --儲存尚未以endObject()完結的newObj if(newObj) then table.insert(newObjList,newObj) end --建立新物件並且設置 繼承 與 無繼承 時的屬性 if(superConstructorCall) then--已經繼承 newObj=superConstructorCall if(superClass~=getObjClass(superConstructorCall)) then error("ERROR: Defining object of class '"..(getClassName(class)or "unknown").."' failed. Argument 'superConstructorCall' of 'object()' should be the constructor function call of class '"..(getClassName(superClass)or "unknown").."'.") end else--無繼承 newObj=Object:new() prepareObjProxy() end local specProps=getSpecProps(newObj) -- print("OBJ:",getClassName(class),newObj) --設置 物件特殊屬性 specProps.class=class specProps.asClass=class--類別備份,在使用asClass()轉換之後此值會變更,增加類別相容度 specProps.isDynamic=false applyObjProxy() return newObj end --[[--將一個類別宣告為抽象類別(抽象類別可以沒有new()方法,有也無法被外部呼叫。因此無法建立實例,只有被繼承之後才可建立實例。) --classFunctionCall:對class()函數的呼叫 --使用範例: local MyObj=abstract(class("MyObj")) func.abstract{Class, function(classFunctionCall) end ,Class}]]-- function gProtectProps.abstract(classFunctionCall) local specProps=getSpecProps(classFunctionCall) if(classFunctionCall and specProps) then local superSpecProps=getSpecProps(specProps.superClass) if(superSpecProps.isAbstract or specProps.superClass==Object) then specProps.isAbstract=true else error("ERROR: Setting class '"..getClassName(classFunctionCall).."' as abstract failed. (Super class should also be an abstract class.)") end else error("ERROR: Require 'class()' function call as argument to 'abstract()'.") end return classFunctionCall end --[[--將一個物件宣告為允許動態附加屬性 --請小心使用 dynamic,因為一旦設置成 dynamic 所有繼承此類別的物件也都將強制繼承 dynamic 的設置。 --objectFunctionCall:對object()函數的呼叫 --使用範例: local myObj=dynamic(object(MyObj)) func.dynamic{Object, function(objectFunctionCall) end ,Object}]]-- function gProtectProps.dynamic(objectFunctionCall) local specProps=getSpecProps(objectFunctionCall) if(objectFunctionCall and specProps) then specProps.isDynamic=true specProps.requireDynamic=true else error("ERROR: Require 'object()' function call as argument to 'dynamic()'.") end return objectFunctionCall end --[[--宣告一個虛擬類別並開始類別定義區塊 --虛擬類別定義區塊同樣使用endClass結尾。 --建立虛擬類別的目的,是把Corona官方API(例如:display.newGroup(),display.newImage()等)所產生的物件整合到類別機制當中。 --原本透過這些API所產生的物件並不屬於任何的類別,這使得這些物件無法被繼承,也無法對他們進行參數型別檢查。而虛擬類別解決了這個問題。 --●重要訊息:盡可能多多實驗虛擬類別的使用。factoryObject與虛擬類別的關係也要搞清楚。等到很有把握,才可以修改現有的虛擬類別。有問題請詢問 Chris (cocoychris@gmail.com)。 --className:虛擬類別名稱 --constructorFunc:作為建構式的函數。例如display.newGroup(),display.newImage()等。 --extendsAbstract:繼承的抽象類別(可為虛擬抽象類別,但不可為一般類別或虛擬類別。) func.virtualClass{String,Function,Class,2, function(className,constructorFunc,extendsAbstract) end ,Class}]]-- function gProtectProps.virtualClass(className,constructorFunc,extendsAbstract) local existClass=virtualClassDict[constructorFunc] local existClass2=virtualClassDict[className] if(existClass) then--已存在 local existClassName=getClassName(existClass) if(className~=existClassName) then print("WARNING: In function 'requireVirtual()'. Virtual class '"..className.."' has already being named as '"..existClassName.."'.") end return existClass elseif(existClass2) then--檢查名稱重複,則傳回相同的類別 return existClass2 else--同樣的虛擬類別不存在,允許建立新的 --檢驗繼承的抽象類別 if(extendsAbstract) then local extSpecProps=getSpecProps(extendsAbstract) if(not extSpecProps.isAbstract) then error("ERROR: Virtual class '"..className.."' cannot extend class '"..getClassName(extendsAbstract).."'(not an abstract class). ") extendsAbstract=nil end end --登記虛擬類別 if(constructorFunc) then virtualClassDict[constructorFunc]=vClass else virtualClassDict[className]=vClass end --建立虛擬類別 local vClass=class(className,extendsAbstract) getSpecProps(vClass).constructorFunc=constructorFunc--將建構函數加入特殊屬性中 return vClass end end --[[--宣告一個虛擬物件並開始物件定義區塊 --虛擬物件定義區塊同樣使用endObject結尾 --vClass:物件的虛擬類別 --constructorFuncCall:對建構函數的呼叫(例如:display.newGroup()),將建立的物件傳入。 func.virtualObject{Class,Table, function(vClass,constructorFuncCall) end ,Object}]]-- function gProtectProps.virtualObject(vClass,constructorFuncCall) local vObject=object(vClass)--建立虛擬物件 if(constructorFuncCall) then local specProps=getSpecProps(newObj) specProps.factoryObject=constructorFuncCall local factoryObject=specProps.factoryObject --此屬性只存在於虛擬類別的實例中。虛擬物件是從原廠物件擴充而成,透過這個屬性可以直接存取到虛擬物件當中的原廠物件(即透過 Corona API所建立的物件) function get:factoryObject() return factoryObject end rawset(vObject,"\_proxy", specProps.factoryObject.\_proxy)--這是原廠物件具有的重要屬性,虛擬物件上必須有同樣的屬性,並指向同一個參照 rawset(vObject,"\_class", specProps.factoryObject.\_class)--這是原廠物件具有的重要屬性,虛擬物件上必須有同樣的屬性,並指向同一個參照 specProps.factoryFuncProxyDict={}--針對原廠物件函數的轉址函數會集中在這。 else error("ERROR: Creating virtual object of class '"..getClassName(vClass).."' failed. Argument #2 ('constructorFuncCall') required.") end return vObject end --[[--結束類別定義區塊,並傳回類別的參考 --建議直接放在return 關鍵字的後方。 --使用範例:return endClass() func.endClass{ function() end ,Class}]]-- function gProtectProps.endClass() local endedClass=newClass if(newClass) then local classSpecProp=getSpecProps(newClass) if(classSpecProp.isAbstract) then if(newClass.new) then--將抽象類別的new方法轉移到保護區 classSpecProp.abstractNew=newClass.new local className=getClassName(newClass) classSpecProp.funcDict.new=function ()error("ERROR: Attempt to create object(call method 'new()') of abstract class '"..className.."'.") end end elseif(not newClass.new) then--確保類別有建構函數 error("ERROR: Class '"..getClassName(newClass).."' don't have a constructor ! Method 'new()' or keyword 'abstract()' is Required ! ") end endObject() newClass=nil else error("ERROR: Unexpected function call 'endClass()'.") end return endedClass end --[[--結束物件定義區塊,並傳回物件的參考 --建議直接放在return 關鍵字的後方。 --使用範例:return endObject() func.endObject{ function() end ,Object}]]-- function gProtectProps.endObject() local specProps=getSpecProps(newObj) if(specProps)then specProps.superProps=nil; if(specProps.requireDynamic and not specProps.isDynamic) then error("ERROR: function 'dynamic()' should be called while defining object "..getObjClassName(newObj)..". Because this object is inherited from a dynamic object.") end end local endedObj=newObj if(newObj) then newObj=table.remove(newObjList) -- print("OBJ END",endedObj,newObj or "ok") else error("ERROR: Unexpected function call 'endObject()'.") end return endedObj end -------------一般方法--------------- --[[--判定是否為指定類別或繼承指定類別。 --object:任意物件 --class:任意類別 func.isClass{Table,Table, function(object, class) end ,Boolean}]]-- function gProtectProps.isClass(object, class) local result=false if(type(object)=="table") then local specProps=object[specialPropAccessKey] if(specProps) then local classSpecProps=specProps.class[specialPropAccessKey] result=classSpecProps.inheritDict[class] or false end end return result end --將某類別作為其繼承樹當中的其他父級類別使用。藉此實現多態。 -- function gProtectProps.asClass( object, class) -- local specProps=object[specialPropAccessKey] -- if(specProps) then -- local classSpecProps=specProps.class[specialPropAccessKey] -- local valid=classSpecProps.inheritDict[class] -- if(valid) then -- specProps.asClass=class -- else -- print("WARNING: Cannot convert object form class "..getObjClassName(object).." to class "..getClassName(class)..".") -- end -- else -- print("WARNING: Calling function 'asClass()' failed. Unknown input object class.") -- end -- return object -- end --[[--取得物件的類別參照 --obj:任意物件 func.getObjClass{Object, function(obj) end ,Class}]]-- function gProtectProps.getObjClass(obj) if(type(obj)=="table" and obj[specialPropAccessKey]) then return obj[specialPropAccessKey].class end end --[[--取得類別名稱 --class:任意類別 func.getClassName{Class, function(class) end ,String}]]-- function gProtectProps.getClassName(class) if(class and class[specialPropAccessKey]) then return class[specialPropAccessKey].name end end --[[--取得物件類別名稱 --class:任意類別 func.getObjClassName{Object, function(obj) end ,String}]]-- function gProtectProps.getObjClassName(obj) local classRef=getObjClass(obj) if(classRef) then return classRef[specialPropAccessKey].name end end --[[--取得 Setter方法的參照 --此函數只能用於物件或類別宣告區塊之中。在覆寫繼承 Setter前,可透過此函數保留繼承的 Setter參照。 --name:Setter名稱 func.getSetter{String, function(name) end ,Function}]]-- function gProtectProps.getSetter(name) local specProps=getSpecProps(newObj); if(specProps)then return specProps.setFuncDict[name]; else error("Calling function 'getSetter' failed. This function can only be used in class or object definition.") end end --[[--取得 Getter方法的參照 --此函數只能用於物件或類別宣告區塊之中。在覆寫繼承 Getter前,可透過此函數保留繼承的 Getter參照。 --name:Getter名稱 func.getGetter{String, function(name) end ,Function}]]-- function gProtectProps.getGetter(name) local specProps=getSpecProps(newObj); if(specProps)then return specProps.getFuncDict[name]; else error("Calling function 'getGetter' failed. This function can only be used in class or object definition.") end end --[[--取得一個 儲存了被覆寫的繼承方法(備份)的 Table。 --只要呼叫過此函數,後續被使用 override關鍵字覆寫的方法,都會被備份在傳回的 table當中。 --注意,此 Table不會備份屬性或 Getter、Setter。如要在覆寫前備份 Getter、Setter,請使用 getGetter()和 getSetter()方法。 --此函數只能用於物件或類別宣告區塊之中。 func.getSuperProps{ function() end ,Table}]]-- function gProtectProps.getSuperProps() local specProps=getSpecProps(newObj); if(specProps)then specProps.superProps=specProps.superProps or {}; return specProps.superProps; else error("Calling function 'getSuperProps' failed. This function can only be used in class or object definition.") end end --[[--宣告類別自身的套件路徑,並在被載入時檢查套件路徑的拼字與大小寫是否正確。 --path:套件路徑,結尾必須包含類別名稱。(與使用 require()的格式相同) func.packagePath{String, function(path) end }]]-- function gProtectProps.packagePath(packagePath) if(package[packagePath] or not package.loaded[packagePath]) then--套件已經被載入此時卻被重複載入,或已載入套件清單當中找不到對應的套件名稱 error("ERROR: Found inconsistent package path while requiring class '"..packagePath.."'. Please check spelling and character cases.") end package[packagePath]=true end --[[--發出錯誤訊息 --obj:錯誤來源物件(接受 nil值) --funcName:錯誤來源方法名稱(接受 nil值) --msg:錯誤訊息 func.err{Object,String,String,1, function(obj,funcName,msg) end }]]-- function gProtectProps.err(obj,funcName,msg) local objString="" if(obj) then objString=tostring(obj) end msg = msg or "" if(obj and funcName) then error("ERROR: Calling '"..funcName.."()' on "..objString.." failed. "..msg) elseif(obj)then error("ERROR: On "..objString.." - "..msg) else error("ERROR: "..msg) end end --[[--發出警告訊息 --obj:錯誤來源物件(接受 nil值) --funcName:錯誤來源方法名稱(接受 nil值) --msg:警告訊息 func.warn{Object,String,String,1, function(obj,funcName,msg) end }]]-- function gProtectProps.warn(obj,funcName,msg) local objString="" if(obj) then objString=tostring(obj) end if(obj and funcName) then print("WARNING: Calling '"..funcName.."()' on "..objString.." failed. "..msg) elseif(obj)then print("WARNING: On "..objString.." - "..msg) else print("WARNING: "..msg) end end --傳回類別 init() return OOP --[[return endClass()]]-- --## sample code -- --\< class定義區塊範例 \> -- local Dog=class("Dog")--定義區塊開始 -- --此處為class定義區塊 -- --通常此處會定義一個new()方法,以便用來產生物件(見下方「object定義區塊範例」) -- --如果不打算在這個區塊內定義new()方法,則應使用abstract()關鍵字。否則會導致錯誤。 -- return endClass()--定義區塊結束 -- -- --\< object定義區塊範例 \> -- 通常會把object定義區塊放在class定義區塊的new()方法中 -- 這樣當類別上的new()被呼叫的時候,就會傳回新的物件。 -- local Dog=class("Dog") -- --此處為class定義區塊 -- --接著使用func關鍵字定義new()方法 -- func.new{ -- function () -- local dog=object(Dog)--object定義區塊開始 -- --此處為object定義區塊 -- return endObject()--object定義區塊結束 -- end -- ,Dog} -- --又回到class定義區塊 -- return endClass() -- -- --------------------- -- --注意:以下範例,請在class或object定義區塊中使用,否則會導致錯誤! -- --------------------- -- --\< getter,setter範例 \> -- local \_testNumber -- --設置getter之後,會使物件增加一個可讀取的屬性 testVar,嘗試讀取此屬性時,就會觸發這個 getter函數而得到傳回的值 \_testNumber。 -- function get:testVar() -- return \_testNumber -- print("被讀取囉~") -- end -- --設置setter之後,會使物件增加一個可寫入的屬性 testVar,嘗試寫入此屬性時,就會觸發這個 setter函數而將數值寫入 \_testNumber。 -- function set:testVar(arg) -- \_testNumber=arg -- print("被寫入囉~") -- end -- -- --\< func關鍵字範例 \> -- --以下使用func關鍵字在物件上建立方法 myFunc,限定參數 a,b皆為Number類型。 -- func.myFunc{Number,Number, -- function (a,b) -- return a+b--傳回a,b相加的結果 -- end -- ,Number}--限定傳回值為Number。
Thanks
Bob
PS: How about the notification problem?