Popularity Contest: Initializing Class-Level Instance Variables

P

Phrogz

Subclassing a class that uses class-level instance variables requires
that you initialize them for subclasses. Which of the following three
techniques do you prefer?


TECHNIQUE 1 - EXPLICIT INITIALIZATION
-------------------------------------
class Alpha
@foo = []
def self.m1; @foo << rand; end
def self.m2; @foo.map{ rand }; end
end

class Bravo < Alpha
@foo = []
end



TECHNIQUE 2 - ON-DEMAND TEST/INITIALIZATION
-------------------------------------------
class Alpha
def self.m1; (@foo||=[]) << rand; end
def self.m2; (@foo||=[]).map{ rand }; end
end

class Bravo < Alpha
# Nothing special here
end



TECHNIQUE 3 - PARENT CLASS HANDLES FOR SUB-CLASSES
--------------------------------------------------
class Alpha
@foo = []
def self.m1; @foo << rand; end
def self.m2; @foo.map{ rand }; end
def self.inherited(subklass)
subklass.instance_eval{ @foo=[] }
end
end

class Bravo < Alpha
# Nothing special here
end
 
A

ara.t.howard

Subclassing a class that uses class-level instance variables requires that
you initialize them for subclasses. Which of the following three techniques
do you prefer?

harp:~ > cat a.rb
require 'rubygems'
require 'attributes'

class A
class << self
attribute 'a' => 42
attribute:)b){ "__#{ self.name }__" } # deferred initialization!
end
end

class B < A
end

p A.a
p A.b
p B.a
p B.b


harp:~ > ruby a.rb
42
"__A__"
42
"__B__"


one can use traits too - but it's pretty heavy if you don't need all it's
features. btw, traits was writtern to solve __exactly__ this common problem.

regards.

-a
 
J

Joel VanderWerf

Phrogz said:
TECHNIQUE 2 - ON-DEMAND TEST/INITIALIZATION
-------------------------------------------
class Alpha
def self.m1; (@foo||=[]) << rand; end
def self.m2; (@foo||=[]).map{ rand }; end
end

class Bravo < Alpha
# Nothing special here
end

I prefer a variant of the above:

class Alpha
def foo
@foo ||= []
end
def self.m1; foo << rand; end
def self.m2; foo.map{ rand }; end
# note that the expression for contstructing foo is not
# repeated twice
end

class Bravo < Alpha
# Nothing special here
end

class Charlie < Alpha
def foo
@foo ||= Set.new # or some other collection class
end
# This implies that all _subclasses_ of Charlie will
# (unless overridden) use a set for the #foo method
end

class Delta < Charlie
# default to Charlie's choice of Set as the foo collection
end

class Echo < Alpha
def initialize(*)
super
@foo = [1,2,3] # possible to specify in advance
end
end
 
J

Joel VanderWerf

Joel said:
Phrogz said:
TECHNIQUE 2 - ON-DEMAND TEST/INITIALIZATION
-------------------------------------------
class Alpha
def self.m1; (@foo||=[]) << rand; end
def self.m2; (@foo||=[]).map{ rand }; end
end

class Bravo < Alpha
# Nothing special here
end

I prefer a variant of the above:

Oops, midway through that exercise I forgot we were talking about class
methods, not instance methods. The following runs correctly:

require 'set'

class Alpha
def self.foo
@foo ||= []
end
def self.m1; foo << rand; end
def self.m2; foo.map{ rand }; end
# note that the expression for contstructing foo is not
# repeated twice
end

class Bravo < Alpha
# Nothing special here
end

class Charlie < Alpha
def self.foo
@foo ||= Set.new # or some other collection class
end
# This implies that all _subclasses_ of Charlie will
# (unless overridden) use a set for the #foo method
end

class Delta < Charlie
# default to Charlie's choice of Set as the foo collection
end

class Echo < Alpha
@foo = [1,2,3] # possible to specify in advance
end

[Alpha, Bravo, Charlie, Delta, Echo].each {|c| p c.m1}

[0.657784608794096]
[0.340975041442558]
#<Set: {0.191071815451883}>
#<Set: {0.971105015466828}>
[1, 2, 3, 0.58602201527139]
 
T

Trans

Phrogz said:
Subclassing a class that uses class-level instance variables requires
that you initialize them for subclasses. Which of the following three
techniques do you prefer?


TECHNIQUE 1 - EXPLICIT INITIALIZATION
-------------------------------------
class Alpha
@foo = []
def self.m1; @foo << rand; end
def self.m2; @foo.map{ rand }; end
end

class Bravo < Alpha
@foo = []
end

Or:

class Alpha
@foo = []
def self.m1; @foo << rand; end
def self.m2; @foo.map{ rand }; end
def self.inherited(base)
base.class_eval { @foo=[] }
end
end

class Bravo < Alpha
end

T.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,579
Members
45,053
Latest member
BrodieSola

Latest Threads

Top