I find this an interesting question, one that I had not given much thought to before today. I consulted the Ruby Style Guide, expecting it would provide some good advice, but it is curiously mute on the subject.
I make four suggestions below. These concern setters as well as getters. Others may disagree and have good reasons for doing so. Let's discuss! I look forward to reading comments.
In reading my remarks it may be helpful to put yourself in the place of someone reading code they wrote some time ago or someone reading someone else's code for the first time.
1. Getters used exclusively within a class should be private
I expect this is my least debatable suggestion, as I can see only disadvantages to making any method public when there is no reason to do so.
2. Methods named after instance variables should not have side effects
By this I mean if there is an instance variable @quantity
, a method named :quantity
should do no more than return the value of @quantity
(i.e., be a getter) and a method named :quantity=
should do no more than assign a value to @quantity
(i.e., be a setter). That is, such methods should not have side effects.
Suppose, for example, a ("pseudo-") setter were defined to assign a value to an instance variable after accounting for a 10% spoilage factor:
def quantity=(q)
0.9 * q
end
If the reader of the code were to miss this definition, but knew there was an instance variable @quantity
, the natural assumption would be that :quantity=
was a setter, which might mask errors or waste time in testing/debugging. Better, I think, would be to write:
class C
attr_writer :quantity
def initialize
end
#...
def adj_quantity_for_spoilage
self.quantity *= 0.9
end
#...
end
c = C.new
c.quantity = 100
c.adj_quantity_for_spoilage
3. Do not use setters within a class
For example, I suggest writing:
class C
attr_accessor :quantity
def change(x)
@quantity = x
end
end
rather than:
class C
attr_accessor :quantity
def change(x)
self.quantity = x
end
end
The main reason is that it is all-to-easy to inadvertently omit self.
in self.quantity = x
, in which case x
would be incorrectly and silently assigned to a newly-created local variable quantity
.
There is a secondary reason in the case where there is no need to access the setter from outside that class. As with getters, we would want setters to be private
in this situation, but that is not possible since they must have the explicit receiver self
. Recall that private methods, by definition, do not have explicit receivers.
Lastly, I see no argument for using self.quantity
over @quantity
, particularly in view of the fact that the former requires the keying of four additional characters.1
4. Do not use getters within a class
I expect this to be my most controversial suggestion and I will be the first to admit that if one rejects it the earth will no doubt continue to orbit the sun. Moreover, I concede that using getters in this way does save the typing of one character, the hard-to-reach-without-looking "@"
.
When I read:
x = m(@quantity)
I know immediately that @quantity
is an instance variable, regardless of my familiarity with the code. If I've forgotten (or never knew) what the variable contains or how it is used, the path to my elucidation is clear. True, if I know there is an instance variable @quantity
, the above has no advantage over:
x = m(quantity)
If, however, I don't know if there is an instance variable @quantity
, I would be faced with three possibilities: quantity
is a local variable, a getter or a method that is not a getter. The time required to complete my investigation should not take much longer than if I were tracking down @quantity
, but those seconds do add up.
Let's consider another thing: what are the consequences of misspelling the name of an instance variable versus misspelling its getter (something I do frequently):
x = m(@qauntity)
versus:
x = m(qauntity)
@qauntity
will return nil
, which may lead to an exception being raised, but possibly not soon or not at all.
qauntity
will almost certainly raise an "no method or variable" exception immediately, giving it the edge in this situation.
In sum, I am suggesting that it generally is best to use getters and setters outside of class definitions only, and that they have no side effects.
Your thoughts?
1. Over a lifetime of coding, typing those four extra characters could amount to hours of time wasted that could otherwise be used productively (e.g., playing pong).