I have a generic Ruby OOP question. Which is the more “correct” way
to share methods between two similar classes that do not necessarily
need to inherit from anything else: inheritance or include (as a mix-
in)? I realize that this question may be subject to opinion; however,
I’m getting ready to formally release a library and I’d like to do the
“right” thing. I like the inheritance route but I’m not sure that
creating a “Base” class that will never be instantiated directly by
anything externally is “proper”.
Here are examples of both approaches:
EXAMPLE 1 (inheritance):
Here Base.new will (should) never be called directly; only through
inheriting classes
class Base
def initialize()
@records = {}
end
def get_record(key) ; @records[key] ; end
def set_record(key, value) ; @records[key] = value ; end
end
class RecType1 < Base ; end
class RecType2 < Base ; end
EXAMPLE 2 (mix-in):
module Base
def init_record()
@records = {}
end
def get_record(key) ; @records[key] ; end
def set_record(key, value) ; @records[key] = value ; end
end
class RecType1
include Base
def initialize()
init_record
end
end
class RecType2
include Base
def initialize()
init_record
end
end
Thanks in advance,
-James
On Mar 15, 1:35 am, [email protected] wrote:
@records = {}
init_record
end
end
class RecType2
include Base
def initialize()
init_record
end
end
When it comes to Ruby there is a huge consideration here: Do you or
will you ever wish to pass on class level methods? Or more technically
speaking, do you want class singleton methods to be in the inheritance
chain? If so then you have to use a class, since modules don’t allow
it (to my eternal dismay). For example:
class X; def self.x; “x”; end; end
class Y < X; end
Y.x #=> x
module X; def self.x; “x”; end; end
class Y; include X; end
Y.x #=> error
IMHO It’s unfortunate that this is the case --or at least that there’s
not a method other than #include that can do it. I guess it’s because
I wrote annotations, which turned out to be very tricky b/c of this.
T.
On Mar 15, 12:11 am, “Trans” [email protected] wrote:
it (to my eternal dismay). For example:
class X; def self.x; “x”; end; end
class Y < X; end
Y.x #=> x
module X; def self.x; “x”; end; end
class Y; include X; end
Y.x #=> error
Modules may not allow it directly, but it seems that you can work
around it easily enough:
module A
def self.included( other )
def other.foo
puts ‘bar’
end
end
end
class B
include A
end
class C < B
end
#> B.foo
#=> bar
#> C.foo
#=> bar
Or am I missing something important here?
Really, the choice of whether to use inheritance or module inclusion
comes down to the question of whether you are sharing identity or just
behavior. For instance, do you want:
class WalkingThing
def walk
puts ‘walking’
end
end
class Human < WalkingThing
end
class Robot < WalkingThing
end
or:
module WalkingThing
def walk
puts ‘walking’
end
end
class Human
include WalkingThing
end
class Robot
include WalkingThing
end
A bit contrived, sure – but hopefully makes the point.
–
Regards,
John W.
On Mar 15, 4:25 am, “John W.” [email protected] wrote:
When it comes to Ruby there is a huge consideration here: Do you or
class Y; include X; end
end
#=> bar
#
#> C.foo
#=> bar
Or am I missing something important here?
Sure. You can do all sorts of tricks to get the desired behavior. The
point is simply “it ain’t natural”.
I don’t really hold a theoretical view of classes and modules. I work
with this on strictly practical level. A simple example is
ActiveSupport’s mattr methods. I haven’t looked at them in a while so
maybe they’ve hacked a solution since then, but if you use mattr in
a module it’ll break if included in a class or other module.
Really, the choice of whether to use inheritance or module inclusion
comes down to the question of whether you are sharing identity or just
behavior. For instance, do you want:
There’s a duck around here that doesn’t know the difference
T.
On 15-Mar-07, at 4:25 AM, John W. wrote:
technically
class Y; include X; end
end
#=> bar
#> C.foo
#=> bar
module A
def self.included(other)
super
other.extend(ClassMethods)
end
module ClassMethods
def foo
puts “bar”
end
end
end
Is a little cleaner I think. And you can do all kinds of other weird
stuff in the self.included method.
Another consideration is that in a single inheritance language like
Ruby, inheritance is valuable. I tend to use mixins until I’m forced
to use inheritance. In a language like Ruby the ‘type hierarchy’
isn’t all that important (duck typing makes it mostly irrelevant).
Cheers,
Bob
Bob H. – blogs at <http://www.recursive.ca/
hutch/>
Recursive Design Inc. – http://www.recursive.ca/
xampl for Ruby – http://rubyforge.org/projects/xampl/
On Mar 15, 12:11 am, “Trans” [email protected] wrote:
module X; def self.x; “x”; end; end
class Y; include X; end
Y.x #=> error
IMHO It’s unfortunate that this is the case --or at least that there’s
not a method other than #include that can do it. I guess it’s because
I wrote annotations, which turned out to be very tricky b/c of this.
In the case of pulling in module methods as class (singleton) methods,
you can use extend instead of include. Your second example rewritten
with “extend” would work:
module X; def x; “x”; end; end
class Y; extend X; end
Y.x #=> “x”
Unfortunately, I’m fairly sure that would require separate modules for
class methods (through extend) and instance methods (through include).
unknown wrote:
module X; def x; “x”; end; end
class Y; extend X; end
Y.x #=> “x”
Unfortunately, I’m fairly sure that would require separate modules for
class methods (through extend) and instance methods (through include).
Although the approach above is the one that I use, for the sake of
completenes there is also this…
irb(main):001:0> module X; def x; “x”; end; end
=> nil
irb(main):002:0> class Y
irb(main):003:1> class << self
irb(main):004:2> include X
irb(main):005:2> end
irb(main):006:1> end
=> #Class:Y
irb(main):007:0> Y.x
=> “x”
On Mar 15, 5:34 am, Bob H. [email protected] wrote:
end
end
Is a little cleaner I think.
Indeed. Both the code and the indentation (I swear I don’t know where
all those spaces came from!) I suppose that’s what I get for trying to
program in an email at the end of a very long day.
On Mar 15, 11:25 am, [email protected] wrote:
In the case of pulling in module methods as class (singleton) methods,
you can use extend instead of include. Your second example rewritten
with “extend” would work:
module X; def x; “x”; end; end
class Y; extend X; end
Y.x #=> “x”
Unfortunately, I’m fairly sure that would require separate modules for
class methods (through extend) and instance methods (through include).
Have a look at module/class_extension in Facets. This sophisticated
code was developed by Daniel S. with the help of a number of
people including myself, Nobu Nakada, Ulysses and Matz.
class Module
alias_method :append_features_without_class_extension, :append_features
= class_extension
Normally when including modules, class/module methods are not
extended. To achieve this behavior requires some clever
Ruby Karate. Instead class_extension provides an easy to use
and clean solution. Simply place the extending class methods
in a block of the special module method #class_extension.
module Mix
def inst_meth
puts ‘inst_meth’
end
class_extension do
def class_meth
“Class Method!”
end
end
end
class X
include Mix
end
X.class_meth #=> “Class Method!”
def class_extension(&block)
@class_extension ||= Module.new do
def self.append_features(mod)
append_features_without_class_extension(mod)
end
end
@class_extension.module_eval(&block) if block_given?
@class_extension
end
private :class_extension
def append_features(mod)
append_features_without_class_extension(mod)
mod.extend(class_extension)
if mod.instance_of? Module
mod.send(:class_extension).send(:include,
class_extension)
end
end
end
class Class
undef_method :class_extension
end
T.
On Mar 15, 7:21 am, “Trans” [email protected] wrote:
Sure. You can do all sorts of tricks to get the desired behavior. The
point is simply “it ain’t natural”.
I guess that’s subjective. I find it completely “natural”.
Really, the choice of whether to use inheritance or module inclusion
comes down to the question of whether you are sharing identity or just
behavior. For instance, do you want:
There’s a duck around here that doesn’t know the difference
The duck might not, but the programmer does. The difference between
identity and behavior can make a complex domain easier to grok if used
appropriately.
Note that I am /not/ a proponent of static typing, and /am/ a
proponent of duck typing. The issue of inheritance vs. inclusion that
I mentioned regards human understanding of the code rather than the
compiler’s.