11

Recently I've been reading "Practical Object Oriented Design in Ruby", and I noticed one of the best practices was to use accessor methods instead of directly grabbing the @instance_variable. For example:

class Foo
  attr_accessor :bar

  def initialize(my_argument)
    @bar = my_argument
  end

  # bad
  # def lorem_ipsum
  #     @bar * 999
  # end

  # good
  def lorem_ipsum
    bar * 999
  end

end

It makes sense to keep things DRY, and, in case I need to process @bar somehow before actually grabbing its value. However, I noticed that the initialize method sets the value of the @bar instance variable directly:

class Foo
  attr_accessor :bar

  def initialize(my_argument)
    @bar = my_argument #<-- why isn't self.bar = my_argument used here?
  end

Is there a reason for this? Shouldn't the setter method be used instead of directly using the = operator to set the value of the instance variable?

the Tin Man
  • 158,662
  • 42
  • 215
  • 303
bigpotato
  • 26,262
  • 56
  • 178
  • 334

4 Answers4

7

You're right, it would make much more sense to do

class Foo
  attr_accessor :bar

  def initialize(my_argument)
    self.bar = my_argument
  end
end

Arguments differ as to whether you should respect encapsulation within the object itself or not, but if you believe in that, then, yes, you should do this.

Jörg W Mittag
  • 363,080
  • 75
  • 446
  • 653
  • 1
    "if you believe in that, then, yes, you should do this.", and do it consistently in your code, otherwise it gets confusing later on when your future self wonders why you were not consistent. – the Tin Man Dec 12 '13 at 16:37
2

The initializer SETS the value upon initialization. The accessor lets you access (read/write) via the symbol after the object is already instantiated.

This post might help you understand: What is attr_accessor in Ruby?

Community
  • 1
  • 1
Justus Eapen
  • 1,139
  • 10
  • 22
  • Can you elaborate? What do you mean use the setter in the initializer? Why would you need to do that if the initializer already has access to the variable? – Justus Eapen Dec 12 '13 at 15:48
  • The book goes to great lengths to explain that you always should use the setter and getter and never access the instance variable directly, yet it blatantly ignores its own advice in the initializer. But the initializer is just a method like any other method, and all the same reasons apply exactly the same. – Jörg W Mittag Dec 12 '13 at 15:51
2

Actually, the setter can be used in initialize the same as in other methods, but setter cannot be used without a receiver.

I think you can use a_foo.bar= or self.bar=, but cannot use bar= without a receiver, because, in the later case, bar will be treated as a local variable rather than a setter method:

class Song
  attr_accessor :name
  def initialize(name)
    self.name = name
  end
  def test_setter1(value)
    @name = value
  end
  def test_setter2(value)
    name = value #name is local variable
  end
end

s = Song.new("Mike")
p s
s.test_setter1("John")
p s
s.test_setter2("Rosy")
p s

This results in:

#<Song:0x23a50b8 @name="Mike">
#<Song:0x23a50b8 @name="John">
#<Song:0x23a50b8 @name="John">
the Tin Man
  • 158,662
  • 42
  • 215
  • 303
uncutstone
  • 408
  • 2
  • 11
1

While you can use the setter in the initialization as shown in @uncutstone's answer, you cannot use it as you've proposed in the comment in your code.

The problem is that Ruby would interpret:

bar = my_argument

as an assignment to the bar local variable rather than an invocation of the bar= method.

This is discussed rather extensively in "Why do Ruby setters need "self." qualification within the class?".

Community
  • 1
  • 1
Peter Alfvin
  • 28,599
  • 8
  • 68
  • 106