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?
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With