From: "Jörg W Mittag" Date: 2007-12-13T12:15:03+09:00 Subject: Re: composition/inheritance/subclassing standard lib classes Hi Bob! bobterwillinger@gmail.com wrote: > An old ruby quiz (#24) deals with a game of poker, and there's a > "Deck" class in a couple of solutions. These classes have an array of > Card objects. > > Similarly, in an example in the Prag porgrammers guide, a SongList > class has an array, which is a list of Song objects. > > To me, it would seem more natural if "Deck" and "SongList" were > subclasses of Array, and added Cards/Songs to self. Does anybody have > an explanation or a good link talking about the merits of these > different approaches? The way I see it, there's two reasons: a more metaphysical design reason and a more practical reason. Let's start with the metaphysical one. In Object-Oriented Design we try to model concepts and entities from the real world with Objects in our programming language. There are correspondences between our Objects and our concepts as well as between the relationships between our Objects and the relationships between our concepts. In particular: an instance-of relationship between an Object and a Class as well as a subclass-of relationship between a Class and another Class correspond to an is-a relationship between concepts or entities. So, to add yet another cats and dogs and cars example to the huge existing pile ... class Mammal; end class Dog < Mammal; end lassie = Dog.new Now Lassie is an instance of Dog, and Dog is a subclass of Mammal. In the real world this corresponds to "Lassie is a Dog" and "Every Dog is a Mammal". If you can get in front of a mirror, look yourself in the eye and say this with a straight face, then making Lassie an instance of Dog and Dog a subclass of Mammal is likely correct, otherwise you should think twice. Now, can you say with a straight face that "Every Deck of Cards is an Array"? Maybe not. So, on to reason number two, the practical reason: Arrays in Ruby are an unwieldy beast. Traditionally, Collections have been very hard to model in classical object-oriented programming languages. Is it Array < OrderedCollectionWithDuplicateEntries < CollectionWithDuplicateEntries or Array < OrderedCollectionWithDuplicateEntries < OrderedCollection? There are many ways to solve this, Multiple Inheritance or Traits for example, but Ruby has found a rather elegant solution: just squish the entire Smalltalk Collection Hierarchy in one single Class! And so, a Ruby Array is not only an Array but also a List (first), a Queue (shift/unshift), a Stack (push/pop), a Vector, a Bag, a Set (uniq, |, &), a Tuple and even a Matrix (transpose)! This power comes with a cost, though: 71 public instance methods on Array (not counting the methods inherited from Object or mixed in from Enumerable)! Do you really want to have all of that on your Deck of Cards? What does "transpose" mean in the context of a SongList? Ruby makes it *very* easy to delegate to an Array instead of inheriting from it, through the use of metaprogramming, Delegator, Forwardable and friends. It also makes it easy to keep the Array "look and feel" on your own objects, because a lot of what looks like Array specific operators are actually just methods (e.g. [], []=, <<) that you can just as well implement on your own class. And thirdly, several useful methods aren't actually methods of the Array Class but of the Enumerable Module that you can mix in into your own class. So, by delegating a couple of methods to an Array and mixing in Enumerable you get something that looks an awful lot like an Array but is actually a Domain Object custom-tailored to your specific needs. An alternate approach if you don't want a specialized Domain Object but rather a specialized generic Collection is described here: . The basic idea is: copy the Array Class (remember, Classes are just Objects, too!), remove the methods that you don't want, et voil�, there is a List or a Stack or a Queue which isn't a subclass of Array but actually "inherits" its behaviour. jwm