Question

I need a class that acts like a Hash, though not necessarily with all the Hash methods. I've read that it is not a good idea to subclass core classes like Hash. Whether or not that is true, what is the best practice for doing this kind of thing?

# (a) subclass Hash, add new methods and instance variables
class Book < Hash
   def reindex
     @index = .....
   end
end

# (b) create a new class from scratch, containing a hash, 
#    and define needed methods for the contained hash
class Book
  def initialize(hash)
    @data = hash
  end 
  def []=(k,v)
    @data[k] = v
  end
  # etc....
  def reindex
    @index = ....
  end

# (c) like (b) but using method_missing

# (d) like (b) but using delegation

I realize that Ruby has more than one way to accomplish a given task, but are there any general rules for which of the above methods are preferable in a relatively simple case?

Was it helpful?

Solution

If I absolutely do not want the Hash-like object to have certain hash methods, then I would wrap the object in my own class and only expose the methods I want it to have (your option b).

If I wanted it to maintain its true hash behavior with some added behavior, I would add that behavior in a module to the hash object itself, not modifying the core hash class:

module SpecialHash
  def reindex
    # method def
  end
end

my_hash = {}
my_hash.extend(SpecialHash)
my_hash.reindex #now is defined on my hash

Most commonly, one of these options will do the trick for me.

In general, I tend to favor using modules to extend class behavior instead of class inheritance because I consider it to be a cleaner and more lightweight approach. Creating a new class always gives me the feeling that I am adding a new "thing" to my domain model. This is fine and is exactly what you want to do in countless scenarios, but Ruby's mixin capabilities give you a very nice alternative when you don't actually need to go quite that far.

The main time when I would deal with creating a class is if there is some additional state in the object that I want to keep track of. If my additions are not about expanding on the state of the object, but are just about expanding on it's behavior, then I will almost always start by mixing that behavior into an existing instance of that class using a module.

Another answer to this sort of question also brings up some other points worth keeping in mind as well: ruby inheritance vs mixins

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top