G
Gavin Kistner
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?
=20
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 =3D ExpensiveObject.new
tmp2 =3D ExpensiveObject.new
# stuff involving tmp1/tmp2
result =3D tmp1 + tmp2 + c2
end
def c2
tmp1 =3D ExpensiveObject.new
tmp2 =3D ExpensiveObject.new
# stuff involving tmp1/tmp2
result =3D 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 =3D ExpensiveObject.new
@tmp2 =3D ExpensiveObject.new
@tmp3 =3D ExpensiveObject.new
@tmp4 =3D ExpensiveObject.new
end
def c1
tmp1, tmp2 =3D @tmp1, @tmp2
#...
end
def c2
tmp1, tmp2 =3D @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 =3D function( ) {
var tmp1 =3D arguments.callee.eo1;
var tmp2 =3D arguments.callee.eo2;
//...
}
Foo.prototype.c1.eo1 =3D new ExpensiveObject;
Foo.prototype.c1.eo2 =3D 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 =3D ( function( )
local tmp1 =3D ExpensiveObject:new( )
local tmp2 =3D 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=3Dgetfenv(inF)}))
end
function Foo:c1( )
-- stuff using tmp1 and tmp2
-- as though they were globals
end
associateFunctionData( Foo.c1, {
tmp1 =3D ExpensiveObject:new( ),
tmp2 =3D ExpensiveObject:new( )
} )
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?
=20
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 =3D ExpensiveObject.new
tmp2 =3D ExpensiveObject.new
# stuff involving tmp1/tmp2
result =3D tmp1 + tmp2 + c2
end
def c2
tmp1 =3D ExpensiveObject.new
tmp2 =3D ExpensiveObject.new
# stuff involving tmp1/tmp2
result =3D 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 =3D ExpensiveObject.new
@tmp2 =3D ExpensiveObject.new
@tmp3 =3D ExpensiveObject.new
@tmp4 =3D ExpensiveObject.new
end
def c1
tmp1, tmp2 =3D @tmp1, @tmp2
#...
end
def c2
tmp1, tmp2 =3D @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 =3D function( ) {
var tmp1 =3D arguments.callee.eo1;
var tmp2 =3D arguments.callee.eo2;
//...
}
Foo.prototype.c1.eo1 =3D new ExpensiveObject;
Foo.prototype.c1.eo2 =3D 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 =3D ( function( )
local tmp1 =3D ExpensiveObject:new( )
local tmp2 =3D 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=3Dgetfenv(inF)}))
end
function Foo:c1( )
-- stuff using tmp1 and tmp2
-- as though they were globals
end
associateFunctionData( Foo.c1, {
tmp1 =3D ExpensiveObject:new( ),
tmp2 =3D ExpensiveObject:new( )
} )