This article by Joey Butler about constants in Ruby got me thinking. How much do we really need constants, anyway?
As Joey points out, constants are an opportunity for implementation details to leak out into other classes. But they complicate things in other ways too.
For instance, I often find myself having to convert a constant to a method when I realize that it needs to be a dynamically calculated value. Why didn’t I just make it a method returning a constant value in the first place?
Constants lead to constant redefinition warnings in dynamically-reloaded code unless you are careful.
Methods are easy to stub out in tests. Constants are a pain to stub out. This becomes an issue if I want to test that a method bases its calculation on the value of a certain constant.
Methods are easy to override on a per-object basis with singleton method definitions or module insertion. Constants are not.
Finally, they are just an extra thing to remember. Was that default value a public constant, or a public class method? I can’t remember, let me go check the docs…
What if we just used methods for everything?
Here’s a module which declares some defaults as methods. Then it uses the little-known #module_function on them. #module_function does two things:
- Makes the methods private
- Makes module-level copies of the methods
We can test that #module_function made module-level copies:[gist id=1154789 file=module_methods.rb]
When we include the module in a class, the class can still use the defaults as normal instance methods:[gist id=1154789 file=sandwich_shop.rb]
We can selectively override defaults in subclasses:[gist id=1154789 file=sub_shop.rb]
And we can even selectively override defaults on a per-object basis:[gist id=1154789 file=special.rb]
Oh, and remember I said that #module_function also makes the methods private? Here’s the proof:[gist id=1154789 file=private.rb]
All in all, using methods for everything is a lot more flexible, and it’s hard to see the down side. Offhand I can only think of two negatives:
- It’s a little more verbose than a constant definition.
- There might be a performance penalty. I haven’t profiled constants vs. methods to find out.
What are your thoughts? Do we need constants?