Because I just had to solve this problem in both JavaScript and Lua, and
because I love Ruby more than either, I thought I’d ask people here how
they’d solve it in Ruby.
The general question is: how would do you associate a few object
instances with methods on a per-method basis?
The Situation/Requirements
A class has 2 different methods.
Each method needs a couple ‘scratch’ objects to perform its
calculations.
The methods call each other; they must not use the same scratch objects.
It is expensive to instantiate a scratch object.
It’s not expensive to initialize an existing scratch object with data.
We like OOP coding, and want to call these as methods of a receiver.
Wasteful Example:
class Foo
def c1
tmp1 = ExpensiveObject.new
tmp2 = ExpensiveObject.new
# stuff involving tmp1/tmp2
result = tmp1 + tmp2 + c2
end
def c2
tmp1 = ExpensiveObject.new
tmp2 = ExpensiveObject.new
# stuff involving tmp1/tmp2
result = tmp1 + tmp2
end
end
The Solution We’ll Ignore
We could create a distinct instance variable for each needed variable.
I personally don’t like the weird coupling this creates, though. So I’m
ignoring it.
class Foo
def initialize
@tmp1 = ExpensiveObject.new
@tmp2 = ExpensiveObject.new
@tmp3 = ExpensiveObject.new
@tmp4 = ExpensiveObject.new
end
def c1
tmp1, tmp2 = @tmp1, @tmp2
#…
end
def c2
tmp1, tmp2 = @tmp3, @tmp4
#…
end
end
The JavaScript Solution
The solution I used for JavaScript was that functions/methods are
first-class objects that can have arbitrary data assigned, and can get a
reference to themselves during execution. So:
Foo.prototype.c1 = function( ) {
var tmp1 = arguments.callee.eo1;
var tmp2 = arguments.callee.eo2;
//…
}
Foo.prototype.c1.eo1 = new ExpensiveObject;
Foo.prototype.c1.eo2 = new ExpensiveObject;
There were a few potential solutions for Lua. I’ll list the two best,
just to help give you ideas:
Lua Solution #1: Closures (would work for JS, too)
Foo.c1 = ( function( )
local tmp1 = ExpensiveObject:new( )
local tmp2 = ExpensiveObject:new( )
return function( self )
– stuff using tmp1 and tmp2
end
end)( )
Lua Solution #2: Changing the global environment for a function
function associateFunctionData( inF, inData )
setfenv(inF, setmetatable(inData,{__index=getfenv(inF)}))
end
function Foo:c1( )
– stuff using tmp1 and tmp2
– as though they were globals
end
associateFunctionData( Foo.c1, {
tmp1 = ExpensiveObject:new( ),
tmp2 = ExpensiveObject:new( )
} )