Better yet, should I be trying to use superclasses for this?
maybe, i don’t think there are any hard and fast rules for deciding when
stuff should be in a module and when it should be in a class, but off
the top of my head, and imho, a few rules of thumb might be:
-
avoid defining instance instance variables (as opposed to module
instance variable) in mixin modules because they can potentialy clash
with instance variables of instances of the class which included the
module. of course it can be argued that method names will also clash,
but instance variables are a little more intractable i think, and also
why double the chances of bugs.
-
use module mixins when you want to be able to enhance classes with
functionality regardless of their class structure.
-
note that in 2), i said functionality and not state. if you want
state a better bet is to go with either inheritance or object
composition.
-
break above rules freely if breaking them makes your life easier. for
instance you might find yorself with two unrelated class hierarchies
which have a common problem of state. if your module is not meant for
widespread use like the Enumerable module, but only within your project
and for your two class hierarchies then it would be convenient and also
DRY to do it via a module mixin, but it should still be considered
carefully as your requirements might change.
Is there a way to do this without having to re-initialize variables in
each inheriting class?
ok i think this question and a few others reveal a fundamental confusion
that you have, so i’ll take it from the beginning. forget modules for a
minute.
class X
@foo = ‘mama’
def imethod
puts @foo
end
def self.cmethod
puts @foo
end
end
in the above example ‘class X’ signals to the ruby runtime that a class
is being built. from then on instance variables can be initialized (@foo
= ‘mama’), methods can be called and class and instance methods can be
defined. note that the instance variable @foo is an instance variable of
class X which is an object like any other.
X.cmethod
prints ‘mama’
x = X.new
x.imethod
prints nil or nothing because @foo has not yet been initialized in the
instance of class X, x.
in other words class instance variables can be initialized and used
either in the body of the class or in class methods, while instance
variables are used and initialized in instance methods.
module M
@foo = ‘papa’
end
class X
include M
def self.cmethod
puts @foo
end
def imethod
puts @foo
end
end
in the above example @foo is initialized in the module body and not by a
method so it is an instance variable of the module. so when you write
X.cmethod
nothing is printed because in cmethod @foo references an instance
variable of X which was never initialized. and when you write
x = X.new
x.imethod
nothing is printed because @foo in this case references an instance
variable of an instance of X, x in which again @foo was never
initialized. note that if i had defined imethod in the module instead,
it would have made absolutely no difference, it would still ‘look for’
an instance variable in x.
i’d recommend getting your head firmly around the above before using a
fancy metaprogramming library. hope my examples clear it up for you.