基於代理伺服器的封裝 |
|
local function noProxyNewIndex() error "Cannot set field in a proxy object" end function makeEncapsulator() -- Returns two tables: The first converts from representations to proxies. The -- second converts the other way. The first will auto-generate new proxies. local proxy2rep = setmetatable( {}, { __mode = "kv" } ) local rep2proxy = {} -- This will be made weak later, but we need to construct more machinery local function genMethod( methods, k ) -- Index function for the __index metatable entry local result = function( proxy, ... ) local rep = proxy2rep[ proxy ] return rep[ k ]( rep, ... ) -- Lua 5.1! end methods[ k ] = result return result end local proxyIndex = setmetatable( {}, { __index = genMethod } ) -- __index table for proxies local function makeProxy( rep ) local proxyMeta = { __metatable = "< protected proxy metatable >", rep = rep, -- GC protection, we won't be able to read this __index = proxyIndex, __newindex = noProxyNewIndex } local proxy = setmetatable( {}, proxyMeta ) proxy2rep[ proxy ] = rep rep2proxy[ rep ] = proxy return proxy end setmetatable( rep2proxy, { __mode = "kv", __metatable = "< protected >", __index = function( t, k ) local proxy = makeProxy( k ) t[ k ] = proxy return proxy end } ) return rep2proxy, proxy2rep end
使用方法如下。我們建立一個封裝器,然後再透過它執行需要封裝的物件。客戶端必須留心在封裝障礙時要封裝一個物件,因為在任何方法中,自我等於真實物件,而非代理伺服器。
local encapsulator = makeEncapsulator() local foo = { hello = function(self) print("Hello from " .. tostring(self)) end } print("foo = " .. tostring(foo)) local efoo = encapsulator[foo] print("efoo = " .. tostring(efoo)) local efoo2 = encapsulator[foo] print("efoo2 = " .. tostring(efoo)) efoo:hello() local baz = { hello = function(self) print("Greetings from " .. tostring(self)) end } print("baz = " .. tostring(baz)) local ebaz = encapsulator[baz] print("ebaz = " .. tostring(ebaz)) ebaz:hello()
請注意,makeEncapsulator
會傳回雙向表格。第二個表格適用於如果需要穿透某些方法呼叫目標以外物件的代理伺服器障礙。
請注意,不應將封裝器表格外洩給不可信的程式碼。proxy2rep
表格有明顯的危險性,因為它會授予對表示的直接存取權。rep2proxy
表格很危險,因為它可以進行反覆處理。可以透過包裝它在另一個層級的代理伺服器表格來處理這個問題,但這會讓封裝物件更昂貴。也可以用函式包裝這個表格,但這又會執行得更慢。
這項實作中沒有任何內容會妨礙使用單一封裝器表格來處理多種物件類型。使用多個封裝器的主要原因會是多個不應能夠看見彼此 repeat 的環境。擁有幾個較小的表格,而非擁有幾個較大的表格,也可能會有速度上的優點。