47

I'm just learning to program and have decided to try Ruby. I'm sure this is a stupid question, but the instructor is talking about setter and getter methods, and I'm confused. Here is the example:

class Human
  def noise=(noise)
    @noise = noise
  end

  def noise
    @noise
  end
end

From this, the class is instantiated, and I can puts this out:

man = Human.new
man.noise=("Howdie!")
puts man.noise

This results in Howdie!

Now what confuses me is that the instructor is saying without the getter method (the 2nd of the two methods), there is no way to interact with the instance variable @noise.

But when I remove the getter method, I'm able to still access @noise, see example:

class Human
  def noise=(noise)
    @noise = noise
  end
end

man = Human.new
puts man.noise=("Howdie!")

This works the same as when the getter method it used.

So now I'm confused. Why is the getter needed? What does the instructor mean by not being able to access the instance variable without it? Is it possible he's using an older version of Ruby?

Thanks in advance for your help.

Ryan Bigg
  • 106,965
  • 23
  • 235
  • 261
Nathan
  • 7,627
  • 11
  • 46
  • 80
  • 1
    Are you really able to access @noise, or is what you're seeing a side effect of the function you're calling... – LVB Jan 05 '12 at 03:55

4 Answers4

35

You can interact with that instance variable from other methods belonging to that instance, even if there is no getter:

def noise=(noise)
  @noise = noise
end

def last_noise
  @noise
end

There doesn't need to be a getter defined with the same name as the method; the two are not linked at all. The getter is needed to "get" the value of the instance variable, but only in a short syntax.

What's happening in your example is that you're initializing a new object (Human.new), and then using a method (noise=, yes the method name contains the = symbol) that just-so-happens to define an instance variable (that is, a variable just for that instance), and then finally retrieving that instance variable with another method call.

You can actually use instance_variable_get to get the instance variable without defining any getter at all:

man = Human.new
man.noise = "Howdie"
man.instance_variable_get("@noise")

This will return "Howdie", even though there is no getter defined.

And no, I don't think he's using an older version of Ruby.

Ryan Bigg
  • 106,965
  • 23
  • 235
  • 261
  • 2
    +1 Ryan. As usual your expertise excels and I didn't know about instance_variable_get ! – Michael Durrant Jan 05 '12 at 04:04
  • hey that doesn't over-ride attr_protected does it? – Michael Durrant Jan 05 '12 at 04:04
  • Answered my own question: - api says "Attributes named in this macro are protected from mass-assignment" – Michael Durrant Jan 05 '12 at 04:06
  • http://ar.rubyonrails.org/classes/ActiveRecord/Base.html#M000350 for attr_accessible and aame for attr_accessible http://ar.rubyonrails.org/classes/ActiveRecord/Base.html#M000351 neither prevent a direct setter – Michael Durrant Jan 05 '12 at 04:08
  • ok attr_readonly(*attributes) Attributes listed as readonly can be set for a new record, but will be ignored in database updates afterwards. nOw that's more like it! – Michael Durrant Jan 05 '12 at 04:10
  • So as I'm understanding it, the setter is an input, once used, passes the value to the instance variable. The getter method references (for lack of a better word) the same variable, so I can call on it to use the variable. But why not just do it in one fell swoop as in my second example, where I give the setter a value and act on it inline? Is there a reason this is avoided? Is there a reason that having a setter/getter pattern is better than doing it inline? – Nathan Jan 05 '12 at 05:08
  • @MichaelDurrant: Just a heads up: foo.rubyonrails.org is currently outdated, per this bug: https://github.com/rails/rails/issues/3559 – Andrew Grimm Jan 05 '12 at 05:51
15

The line of code

puts man.noise=("Howdie!")

does NOT use the getter method, so the getter method does not need to be defined for it to work. That line just uses the setter method. The return value of the setter method is automatically equal to whatever is on the right-hand side of the equal sign, so "Howdie!" gets passed to puts.

The line of code

puts man.noise

DOES use the getter method, and it would not work if you remove the getter method.

David Grayson
  • 84,103
  • 24
  • 152
  • 189
  • So I guess my question is, why use the getter & setter methods? Why not just use the setter method (or basic method that lets me pass a value to the method) and have a "return" on that? I'm not suggesting a better way of doing things, I'm genuinely asking why? I don't understand. – Nathan Jan 05 '12 at 05:00
  • 1
    You need the getter method because sometimes you want to get the value of the variable without changing it. The setter method always sets the value to something, so it is impossible to use the setter method to get the value without modifying it! – David Grayson Jan 05 '12 at 17:23
  • You only need the getter method if you want the variable to be readable by methods outside of the class. If you just want to read the variable from a method in the class, you can simply use `@noise` instead of the getter method. – David Grayson Jan 05 '12 at 17:25
  • This answer is the best for me. getter versus setter is the key of the answer. If I understand, the getter method is helpfull only if there is a default value of noise inside the class. If there is no default value we don't need it – kouty Dec 11 '16 at 19:42
  • If one writes `man.noise ||= value` then the getter and setter methods both need to be defined. I think that's what you might be saying. – David Grayson Dec 11 '16 at 19:51
5

Surely they both return a value, but their behavior are different.

Let's say there is already a member @a .

with getter, one obtains the current value of @a, without modifying it.

with setter, one modifies @a, and get its new value as return value.

when thinking about behavior of setter, note:

  1. the old value of @a can not be obtained with setter, and got overwritten.

  2. what returned by setter, is actually already known before invoking setter.

Jokester
  • 5,501
  • 3
  • 31
  • 39
  • I didn't understand your reply until I read everyone else's, now that I did (and thought a bit more), I think your answer helps drive home the question I had. If you beef up your answer so it's reduces the need for newbies like me to read everyone else's answers first, then I'd like to select yours. – Nathan Jan 05 '12 at 05:30
1

May be the attention for getters and setters is because some other languages allow you to access class variables directly. Python:

class k:
    i = 100

print k.i # 100
k.i = 200
print k.i # 200

In contrast, Ruby keeps all of its variables completely private to the class and only exposes them through accessor methods.

In your example, if you remove the getter, you can indeed interact with the variable (that is: change it) trough the setter, but you can't get it back (in a regular way) when you need it.

steenslag
  • 79,051
  • 16
  • 138
  • 171