
Written August 18, 2013. Tagged Ruby.
Inheriting from String as a shortcut for classes that initialize with strings isn't necessarily a great idea.
I've seen it used most recently to define a DNA class on exercism.io, e.g.
class DNA < String
def to_rna
gsub("T", "U")
end
end
dna = DNA.new("GATTACA")
dna.to_rna # => "GAUUACA"This is often misguided for much the same reasons that inheriting from Struct is.
Your subclass will happily do this:
dna = DNA.newUnlike with Struct inheritance, a missing argument won't cause a method like #to_rna to explode with nil errors, as self will still be an empty string. Instead of exceptions you risk unexpected behavior, like empty strands of DNA kicking around your application. Exploding would be preferable.
By subclassing String you suggest that DNA is a specialized string.
It will get a ton of methods in its API, such as #upcase!, #match, #valid_encoding? and #each_line. Do those make sense for your class?
With the DNA class above, it's impossible to tell what subset of methods you intended to inherit and think make sense for this class. Only the initializer? Also #to_s? You should only inherit a class when all its methods make sense for the subclass.
If it's the initializer you want, just write your own or use some library to reduce boilerplate. If it's some other method, delegate those to a string – composition instead of inheritance. It will make the API of your class clearer.
With DNA, it's probably reasonable that DNA.new("GATTACA") == DNA.new("GATTACA"). If that's not how you want identity to work, though, be aware that your class will inherit this behavior.
Steve Klabnik mentions some gotchas of inheriting a core class like String.
#to_s is not called implicitly with interpolation. Your initializer won't always be called.
You won't trigger these gotchas most of the time, but some time you might, and it's easily avoided by not inheriting from String.