General Ruby OOP Question - using inheritance or include for


#1

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


#2

On Mar 15, 1:35 am, removed_email_address@domain.invalid 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.


#3

On Mar 15, 12:11 am, “Trans” removed_email_address@domain.invalid 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.
http://johnwilger.com


#4

On Mar 15, 4:25 am, “John W.” removed_email_address@domain.invalid 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 :wink:

T.


#5

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/


#6

On Mar 15, 12:11 am, “Trans” removed_email_address@domain.invalid 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).


#7

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”


#8

On Mar 15, 5:34 am, Bob H. removed_email_address@domain.invalid 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. :frowning:


#9

On Mar 15, 11:25 am, removed_email_address@domain.invalid 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.


#10

On Mar 15, 7:21 am, “Trans” removed_email_address@domain.invalid 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”. :slight_smile:

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 :wink:

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.