A post the other day on /r/ruby asked about the reasoning behind the recommendation that instance variables be accessed through methods instead of directly throughout code where they are in scope. Most of the responses dealt the fact that this helps make your code more maintainable, which is true, but I think there’s a more significant reason for this: it adds data to your class’s public interface, so it encourages healthy design habits and forces some introspection.
In a carefully designed class, changes to interface are taken seriously. Before you add, remove, or change it, you should ask some questions, the most important (in my opinion) of which are, “Is this method reasonable, given the stated purpose of this class?” and “Is this a method something that users can trust to be there and work properly in future releases of this library?” When you expose an instance variable as a method, you implicitly respond to both of those statements in the affirmative, so think carefully about whether those questions are also true of that instance variable!
When you find yourself in the position that you have an instance variable that shouldn’t be exposed as part of the public API, consider that you might have a class with too much responsibility or an instance variable that you don’t need. Unneeded instance variables often come from doing this:
def my_process get_foo use_foo_here use_foo_again return_value end def get_foo @foo = ObjectLookup.find_by(quality: 'foo') end def use_foo_here @bar = @foo + 10 end def use_foo_again @baz = @bar + 20 end def return_value @bar > 40 ? @bar : @foo end
…when something like this is healthier in every way:
def my_process foo = get_foo bar = plus_10(foo) baz = plus_20(bar) return_value(foo, baz) end def get_foo ObjectLookup.find_by(quality: 'foo') end def plus_10(obj) obj + 10 end def plus_20(obj) obj + 20 end def return_value(starting, ending) ending > 40 ? ending : starting end
Yes, there are times whenyou want to reuse an expensive query multiple times internally within an object and exposing it to the public API would be inappropriate. That’s fine, just mark that method private. But that should be your last result, not an excuse for extra data. Changes to the public API are an opportunity to question your approach and improve the quality of your code, so face them head on!