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 2007-03-15 06:36
on 2007-03-15 08:12
On Mar 15, 1:35 am, james.d.mast...@gmail.com 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 2007-03-15 09:25
On Mar 15, 12:11 am, "Trans" <transf...@gmail.com> 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 Wilger http://johnwilger.com
on 2007-03-15 13:35
On 15-Mar-07, at 4:25 AM, John Wilger 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 Hutchison -- blogs at <http://www.recursive.ca/ hutch/> Recursive Design Inc. -- <http://www.recursive.ca/> xampl for Ruby -- <http://rubyforge.org/projects/xampl/>
on 2007-03-15 15:22
On Mar 15, 4:25 am, "John Wilger" <johnwil...@gmail.com> 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 2007-03-15 16:25
On Mar 15, 12:11 am, "Trans" <transf...@gmail.com> 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).
on 2007-03-15 21:20
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 2007-03-15 23:20
On Mar 15, 11:25 am, james.d.mast...@gmail.com 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 Schierbeck 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 2007-03-16 02:51
On Mar 15, 5:34 am, Bob Hutchison <h...@recursive.ca> 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 2007-03-16 03:01
On Mar 15, 7:21 am, "Trans" <transf...@gmail.com> 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.
Please log in before posting. Registration is free and takes only a minute.
Existing account
(Switch to SSL-encrypted connection)
NEW: Do you have a Google/GoogleMail or Yahoo account? No registration required!
Log in with Google account | Log in with Yahoo account
Log in with Google account | Log in with Yahoo account
No account? Register here.