Inheriting from base classes

Hi –

Gavin Sinclair and I have been involved in an interesting exchange
about the merits (or lack thereof) of inheriting from base classes, in
particular String/Array/Hash. This is also something that some of us
were chatting about at the conference in November.

We’re both very interested in hearing what people’s views are on this.
Do you ever subclass String, Array, and Hash? Do you specifically
avoid doing so?

A brief summary of some of what we’ve been saying follows:

The argument in favor is that core classes already do a lot, and
specializing them can streamline things and economize on code. For
example:

class ClassRoster < Array

class AssignmentValues < Hash

class ExamQuestion::Title < String

as opposed to:

class ClassRoster
def initialize
@actual_roster = []
end
end

and so on.

On the other hand, objects end up with more methods than will actually
be used. There’s a kind of possible looseness to the fit.

However… objects always at least potentially have "extra"
functionality in Ruby. Is there any reason not to rely on the
documentation of an interface? Don’t we implicitly do that anyway
(since people can always add methods to objects and do unexpected
things, if that’s what they’re inclined to do)?

There’s also some question as to whether classes that inherit from
base classes should themselves be fairly generic, like

class OrderedHash < Hash

which also raises the question of where the boundary is between
generic and non-generic…

Thoughts?

David

···


David Alan Black
home: dblack@candle.superlink.net
work: blackdav@shu.edu
Web: http://pirate.shu.edu/~blackdav

The normal test for subclassing is to see if the question “Is a Y is a
kind of X?” is true for the proposed subclass, when you talk about the
concepts that the subclass represents.

Is a ClassRoster a kind of Array? Probably false. From the name I’d
guess that ClassRoster represents the group of people attending the
class, and alternate implementations not based on Array are probably
just as useful.

Are AssignmentValues a kind of Hash? Probably false. I cannot guess
from the name what it might represent.

Is an OrderedHash a kind of Hash? Probably true. The name suggests
some sort of ordering inside the Hash concept.

IMNSHO, subclassing String should only happen when you really and truly
have something that is a special kind of String that completely
conforms to all of the tests that String supports (a Liskov
substitution principle thing). This might be true for a
"SuperEfficientStringImplementation" that just changes the time/space
tradeoffs for the current string class, or maybe for a ReversibleString
that adds a few extra behaviors to String.

Pete

···

On Monday, February 10, 2003, at 07:40 PM, dblack@candle.superlink.net wrote:

Gavin Sinclair and I have been involved in an interesting exchange
about the merits (or lack thereof) of inheriting from base classes, in
particular String/Array/Hash. This is also something that some of us
were chatting about at the conference in November.

We’re both very interested in hearing what people’s views are on this.
Do you ever subclass String, Array, and Hash? Do you specifically
avoid doing so?


Pete McBreen, McBreen.Consulting , Cochrane, AB
http://www.mcbreen.ab.ca/

Author, "Software Craftsmanship The New Imperative"
Winner of 2002 SD Productivity Award

Author: "Questioning Extreme Programming"
Addison-Wesley © 2003

We’re both very interested in hearing what people’s views are on this.
Do you ever subclass String, Array, and Hash? Do you specifically
avoid doing so?

Seems in practice, whenever I have a need to do something with a String,
Hash or Array that isn’t built-in, then I just tack it on, a la:

class String
def every_other_char
# yada yada
end
end

… I don’t ever subclass or wrap. And I love being able to do this – this
for me is one thing that makes Ruby fun.

The risk with this, I suppose, is a name-clash with some other code that
also defines every_other_char and does something a little different with it
… but the scope of my work is limited to probably not ever have this be an
issue.

Chris
http://clabs.org

In article Pine.LNX.4.44.0302102129370.5712-100000@candle.superlink.net,

Hi –

Gavin Sinclair and I have been involved in an interesting exchange
about the merits (or lack thereof) of inheriting from base classes, in
particular String/Array/Hash. This is also something that some of us
were chatting about at the conference in November.

We’re both very interested in hearing what people’s views are on this.
Do you ever subclass String, Array, and Hash? Do you specifically
avoid doing so?

A brief summary of some of what we’ve been saying follows:

The argument in favor is that core classes already do a lot, and
specializing them can streamline things and economize on code. For
example:

class ClassRoster < Array

I’m more likely to think that ClassRoster ‘has-a’ Array then to think that
ClassRoster ‘is-a’ Array. In this case I would use aggregation.

class AssignmentValues < Hash

Not quite sure from the name of the class, but I’ll guess it has something
to do with the previous context and assume that AssignmentValues might
have something to do with the type of class you take in school… if so,
then again, I don’t see how it ‘is-a’ hash, but instead that it 'has-a’
hash. Again, I’d use aggregation.

class ExamQuestion::Title < String
This one could go either way, I suppose, but again, I’d lean toward the
’has-a’.

as opposed to:

class ClassRoster
def initialize
@actual_roster = []
end
end

I think this is fine- ‘has-a’ Array works here.

and so on.

On the other hand, objects end up with more methods than will actually
be used. There’s a kind of possible looseness to the fit.

However… objects always at least potentially have "extra"
functionality in Ruby. Is there any reason not to rely on the
documentation of an interface? Don’t we implicitly do that anyway
(since people can always add methods to objects and do unexpected
things, if that’s what they’re inclined to do)?

There’s also some question as to whether classes that inherit from
base classes should themselves be fairly generic, like

class OrderedHash < Hash
This seems to be a valid case for using inheritance: An OrderedHash 'is-a’
kind of Hash.

which also raises the question of where the boundary is between
generic and non-generic…

Thoughts?

In thinking back over the last couple of years that I’ve been using Ruby,
the cases where I’ve subclassed builtins like Array, Hash or String
have been very rare.

I’m much more likely to favor aggregation (‘has-a’) over inheritance
(‘is-a’) - as in the examples above. You can often use 'method_missing’
to redirect method calls to the contained object.

Also, if I need to add functionality to one of the built-in classes, I
generally just add a method to the class instead of inheriting from it.
That’s kind of a controversial practice, though…

Phil

···

dblack@candle.superlink.net wrote:

Hi –

Gavin Sinclair and I have been involved in an interesting exchange
about the merits (or lack thereof) of inheriting from base classes, in
particular String/Array/Hash. This is also something that some of us
were chatting about at the conference in November.

We’re both very interested in hearing what people’s views are on this.
Do you ever subclass String, Array, and Hash? Do you specifically
avoid doing so?

> > Thoughts?

I subclass whenever I’m going to need the functionality provided by the base
class. For instance, if the object being modeled fits into an array, (I’m not
sure a class roster would), and I’m going to need to iterate over it, then I’ll
subclass Array, then, I get Array#each “for free.” Why implement ‘each’ again
in my class when I can just use Array’s? For a silly, but illustrative,
example:

class MyClass < Array
def initialize
0.upto(9) { |i|
self[i] = i+1
}
end

def onlyEvens
self.each {|x| yield x if x % 2 == 0}
end
end

c = MyClass.new
c.each {|x| puts x }
c.onlyEvens {|e| puts e}

I love getting “for free” functionality. For me, this is one of the "fun"
things about Ruby.

Look at GOTOU Yuuzou’s ‘icmp’ module for some interesting uses of subclassing
String.

···

On Tue, 11 Feb 2003 11:40:14 +0900 dblack@candle.superlink.net wrote:

David


David Alan Black
home: dblack@candle.superlink.net
work: blackdav@shu.edu
Web: http://pirate.shu.edu/~blackdav


Daniel P. Zepeda

The ImageList class in RMagick is a subclass of Array. An ImageList is
an array of images, plus an index value that addresses the "current"
image.

Yes, there’s a “looseness of fit.” There’s a handful of methods that
just don’t apply to an array of images: to_s, pack, etc. Since by
definition an ImageList can only contain images, assoc, flatten, and
such-like also don’t apply. These methods are undefined in ImageList.

There’s a method in ImageList for every Array method. Array methods
that return Arrays (+, for example) call “self.class.new.replace” to
convert the Arrays that the super returns into ImageLists. (Yes,
ImageList#replace gets a lot of work.)

Adding an Array to an ImageList works, as long as the array contains
only images, and yields an ImageList. Adding an ImageList to an Array
yields an Array, which is what I think you’d expect.

My first cut at ImageList had ImageList contain an array of images
and the usual accessor methods for the array, but this was just too
clunky. For example,

mylist.images << newimage

is clunky compared to

mylist << newimage
···

On Tue, 11 Feb 2003 11:40:14 +0900, dblack@candle.superlink.net wrote:

We’re both very interested in hearing what people’s views are on this.
Do you ever subclass String, Array, and Hash? Do you specifically
avoid doing so?

I think RubyŽs design favors aggregation over inheritance heavily. [I understand matz does too :)]

ThereŽs a number of things against inheritance, in general:

  • you have to know things about the implementation of superclasses
    since instance variables are not class-local
  • the probability of name clashes is too high if you add in modules and
    stuff
  • delegation is so convenient in Ruby you tend to forget about
    inheritance, since subclassing is a very serious decision you cannot
    take lightly
  • in general (although a little less pertinent to Ruby) designs based
    on object relationships (such as aggregation) are more flexible than
    those based on class relationships (more static, esp. in statically
    typed languages)

Moreover, the examples you gave really donŽt seem at all to satisfy the
Žis-aŽ rule of thumb. IŽd definitely go with aggregation there.

···

On Tue, Feb 11, 2003 at 11:40:14AM +0900, dblack@candle.superlink.net wrote:

Hi –

Gavin Sinclair and I have been involved in an interesting exchange
about the merits (or lack thereof) of inheriting from base classes, in
particular String/Array/Hash. This is also something that some of us
were chatting about at the conference in November.

We’re both very interested in hearing what people’s views are on this.
Do you ever subclass String, Array, and Hash? Do you specifically
avoid doing so?


_ _

__ __ | | ___ _ __ ___ __ _ _ __
’_ \ / | __/ __| '_ _ \ / ` | ’ \
) | (| | |
__ \ | | | | | (| | | | |
.__/ _,
|_|/| || ||_,|| |_|
Running Debian GNU/Linux Sid (unstable)
batsman dot geo at yahoo dot com

MS-DOS, you can’t live with it, you can live without it.
– from Lars Wirzenius’ .sig

My feeling was always that it was only OK to subclass a class if it would be
reasonable to substitute an instance of your new class for an instance of
the older one.

Another way of saying this: if obj.kind_of?(Array), then it should be
reasonable and even expected that I use obj as an array. This is why
SomeOptimizedArray is ok, but ClassRoster is not.

`kind_of?’ integrity is important, in my opinion; it’s totally fundamental
to the whole idea of the inheritance heirarchy.

Chris

Some more reasons to avoid inheriting from concrete classes:

[ruby-talk:21745]
[ruby-talk:40925]
[ruby-talk:32561]
[ruby-talk:52740]
[ruby-talk:58913]
[ruby-talk:55261]
[ruby-talk:8462]

(note that inheriting from base classes is always okay; I’d love to see
an example of someone inheriting from a class that is not a base class
:).

Paul

“Phil Tomson” ptkwt@shell1.aracnet.com schrieb im Newsbeitrag
news:b2a6o001rma@enews4.newsguy.com

I’m much more likely to favor aggregation (‘has-a’) over inheritance
(‘is-a’) - as in the examples above. You can often use 'method_missing’
to redirect method calls to the contained object.

I’d support has-a vs. is-a if is-a is not 100% clear. Composition with an
Array is very much simplified by using Enumerable, so the effort to do the
forwarding is very low, because it needs only an implementation of each
like this:

irb(main):109:0> class ArrayContainer
irb(main):110:1> include Enumerable
irb(main):111:1>
irb(main):112:1* def initialize()
irb(main):113:2> @arrayMember = [1,2,3]
irb(main):114:2> end
irb(main):115:1>
irb(main):116:1* def each()
irb(main):117:2> @arrayMember.each {|x| yield x}
irb(main):118:2> end
irb(main):119:1> end
nil
irb(main):120:0>
irb(main):121:0* a = ArrayContainer.new
#<ArrayContainer:0x2aafa10 @arrayMember=[1, 2, 3]>
irb(main):122:0> a.to_a
[1, 2, 3]
irb(main):123:0> a.each { |x| p x }
1
2
3
[1, 2, 3]
irb(main):124:0>

Regards

robert

Hi –

class AssignmentValues < Hash

Not quite sure from the name of the class, but I’ll guess it has something
to do with the previous context and assume that AssignmentValues might
have something to do with the type of class you take in school… if so,
then again, I don’t see how it ‘is-a’ hash, but instead that it 'has-a’
hash. Again, I’d use aggregation.

Sorry for using an opaque example. What I meant by AssignmentValues
was essentially a hash of assignment names (exams, papers, etc.) and
the percentage of the final grade they count for.

In thinking back over the last couple of years that I’ve been using Ruby,
the cases where I’ve subclassed builtins like Array, Hash or String
have been very rare.

I’m much more likely to favor aggregation (‘has-a’) over inheritance
(‘is-a’) - as in the examples above. You can often use 'method_missing’
to redirect method calls to the contained object.

True… but that kind of thing sometimes just leads me to wonder, why
not just build the class itself on the class it is (to a large extent)
trying to behave like?

Also, if I need to add functionality to one of the built-in classes, I
generally just add a method to the class instead of inheriting from it.
That’s kind of a controversial practice, though…

It’s also kind of different, in that it doesn’t give you new and
differentiated (from each other) classes. I wouldn’t want to add
ClassRoster’s specialized methods to Array, even if I decided
ClassRoster should be built on top of Array.

David

···

On Tue, 11 Feb 2003, Phil Tomson wrote:


David Alan Black
home: dblack@candle.superlink.net
work: blackdav@shu.edu
Web: http://pirate.shu.edu/~blackdav

Hi –

The normal test for subclassing is to see if the question “Is a Y is a
kind of X?” is true for the proposed subclass, when you talk about the
concepts that the subclass represents.

I have a Ruby-specific question about this, which I ask with a bit of
trepidation since I know that OO principles at this level of
abstraction are generally not language-specific…

In Ruby, it seems to me that the module mechanism does a lot to reduce
the importance of the class hierarchy tree. Or, to relate it to your
point, modules add adjectives to the mix. So "Is a Y a kind of X?"
might come out as, “Is a Y a Z-able X?” And the answer to this kind
of question might (?) more often be “Yes.”

Is a ClassRoster a kind of Array? Probably false. From the name I’d
guess that ClassRoster represents the group of people attending the
class, and alternate implementations not based on Array are probably
just as useful.

But then there’s the question: if a ClassRoster is essentially an
ordered list of Student objects, and that list is going to be added
to, sorted, subtracted from, mapped over, etc. etc… what is the
disadvantage to having it subclass Array (rather than sort of shadow
Array via instance variables and rewriting of methods that Array
already has)? I’m willing to believe there are disadvantages, but I’m
not succeeding in picturing an actual scenario where it would play out
badly.

And, again, there’s the presence of modules. One might say: a
ClassRoster is a Gradeable Array, or some such.

Are AssignmentValues a kind of Hash? Probably false. I cannot guess
from the name what it might represent.

Basically:

Midterm exam => 30%
Term Paper => 30%
Final Exam => 40%

kind of thing. A better name might be CourseRequirements.

David

···

On Tue, 11 Feb 2003, Pete McBreen wrote:


David Alan Black
home: dblack@candle.superlink.net
work: blackdav@shu.edu
Web: http://pirate.shu.edu/~blackdav

Why not define your own << method? E.g.:

class ImageList
def <<(other)
images << other
end
end

···

On Tue, Feb 11, 2003 at 11:18:40PM +0900, Tim Hunter wrote:

My first cut at ImageList had ImageList contain an array of images
and the usual accessor methods for the array, but this was just too
clunky. For example,

mylist.images << newimage

is clunky compared to

mylist << newimage

`kind_of?’ integrity is important, in my opinion; it’s totally
fundamental
to the whole idea of the inheritance heirarchy.

I agree with this, and try to stick with it. Inheriting because it
reduces code generally will bite me later. Often too it exposes
methods in the base class that I don’t really want in the subclass,
forcing me to disable them in some way.

Using aggregation where I don’t feel inheritance is appropriate works
ok with my XP leanings as well, as I can just delegate calls to the
aggregatee as I actually need them, rather than trying to figure out
ahead of time what all the methods I’ll need to disable are.

···

=====

Yahoo IM: michael_s_campbell


Do you Yahoo!?
Yahoo! Shopping - Send Flowers for Valentine’s Day
http://shopping.yahoo.com

Hi –

`kind_of?’ integrity is important, in my opinion; it’s totally fundamental
to the whole idea of the inheritance heirarchy.

But a further question is: how fundamental is the inheritance hierarchy
to designing programs in Ruby, given the non-kind_of? underpinnings of
Ruby objects (i.e., their dynanism and “in-the-moment” behavior)?

I keep thinking back to a comment by Matz, in the midst of a related
discussion:

I feel you’re relying too much on inheritance hierarchy. In Ruby,
it’s at best implementation sharing.

(ruby-talk:19878)

David

···

On Wed, 12 Feb 2003, Chris Pine wrote:


David Alan Black
home: dblack@candle.superlink.net
work: blackdav@shu.edu
Web: http://pirate.shu.edu/~blackdav

Some more reasons to avoid inheriting from concrete classes:

(note that inheriting from base classes is always okay; I’d love to see
an example of someone inheriting from a class that is not a base class
:).

Paul

Sorry for being ignorant, but I’m confused. What do you mean by
’concrete’ vs. ‘base’ classes? When David started this thread he
said:

Gavin Sinclair and I have been involved in an interesting
exchange about the merits (or lack thereof) of inheriting
from base classes, in particular String/Array/Hash.

Some of the examples you cited included String, and Array (base
classes). Your examples show problems inheriting from String and
Array, yet above you say it is always okay. Could you clarify?
Thanks,

  • Jim -
···

On Thu, 13 Feb 2003 05:59:52 +0900, Paul Brannan wrote:

The biggest disadvantage I see is that you are exposing many more
methods than you’d like. One side effect of this is the following: an
Array in Ruby can hold any kind of object, but a ClassRoster holds only
Students. So any method that can add elements to the Array might want
to make sure it is adding only Students. Providing checks like this is
ugly and unproductive, particularly since it would change the behavior
of the base class.

It’s easier, IMO, to provide a minimal interface (each() and [] and []=)
and then mixin the Enumerable module.

Paul

···

On Tue, Feb 11, 2003 at 11:26:50PM +0900, dblack@candle.superlink.net wrote:

But then there’s the question: if a ClassRoster is essentially an
ordered list of Student objects, and that list is going to be added
to, sorted, subtracted from, mapped over, etc. etc… what is the
disadvantage to having it subclass Array (rather than sort of shadow
Array via instance variables and rewriting of methods that Array
already has)? I’m willing to believe there are disadvantages, but I’m
not succeeding in picturing an actual scenario where it would play out
badly.

Hi –

My first cut at ImageList had ImageList contain an array of images
and the usual accessor methods for the array, but this was just too
clunky. For example,

mylist.images << newimage

is clunky compared to

mylist << newimage

Why not define your own << method? E.g.:

class ImageList
def <<(other)
images << other
end
end

My answer to that is that it dramatically increases the amount of code
involved, and always feels “extra” to me. I sometimes start out doing
things like that, and then move toward just inheriting from an
existing class.

It’s not impossible that I need to re-read the delegation
chapters… :slight_smile:

David

···

On Tue, 11 Feb 2003, Paul Brannan wrote:

On Tue, Feb 11, 2003 at 11:18:40PM +0900, Tim Hunter wrote:


David Alan Black
home: dblack@candle.superlink.net
work: blackdav@shu.edu
Web: http://pirate.shu.edu/~blackdav

If I were writing code that needed to perform all those operations on
class rosters, I would be inclined to do this:

class_roster = []

Then you can apply all the Array methods you like! :slight_smile:

But you want some ClassRoster-specific methods. I’d be looking at
hiding the complex manipulations of the class roster from the outside
world.

class ClassRoster
def initialise
@roster = []
end

def foo
  @roster.manipulate
end

...

end

That’s what you should be aiming for in OO code: users don’t have to
do any low-level stuff on your objects. You implement just the
methods they need. They shouldn’t have to know that Array is used to
implement it.

Of course, you need a way of building up a roster. That might be by
parsing a text file, or you may need to resort to:

class ClassRoster
def <<(o)
@roster = []
end
end

Rather than say “gosh, what a pain doing such a boring method”, look
for ways to eliminate the need for it. Or, if a few such methods are
indeed perfectly justifiable, then use one of Ruby’s forwarding
mechanisms, or try to do more than just delegate (error checking, for
instance).

At the end of the day, the fewer methods per class, the better.
Superfluous methods cloud the intention of the class. (String et al
are excluded from this rule for obvious reasons.)

Gavin

···

On Wednesday, February 12, 2003, 1:26:50 AM, dblack wrote:

[On ClassRoster < Array]

But then there’s the question: if a ClassRoster is essentially an
ordered list of Student objects, and that list is going to be added
to, sorted, subtracted from, mapped over, etc. etc… what is the
disadvantage to having it subclass Array (rather than sort of shadow
Array via instance variables and rewriting of methods that Array
already has)? I’m willing to believe there are disadvantages, but
I’m not succeeding in picturing an actual scenario where it would
play out badly.

But a further question is: how fundamental is the inheritance
hierarchy
to designing programs in Ruby, given the non-kind_of? underpinnings
of
Ruby objects (i.e., their dynanism and “in-the-moment” behavior)?

Are you saying here that because it’s easy to not do strict kind-of?
modelling in ruby, that you shouldn’t? Or that existing classes in
ruby don’t do it, so end users shouldn’t have to? Or something else?

Your previous post seemed to indicate you break the kind-of? “rule of
thumb” where it becomes convenient to save typing, which I think is
not the way it should be done. not that I’ve never done the same
thing, but we’re at a more conceptual level with this discussion.
(Or are we? maybe that’s a disconnect.)

···

=====

Yahoo IM: michael_s_campbell


Do you Yahoo!?
Yahoo! Shopping - Send Flowers for Valentine’s Day
http://shopping.yahoo.com