7

In ruby you can internally access variables directly via @var_name or via private getters attr_reader :var_name.

Which solution is more (semantically?) correct? Any advantages/disadvantages of using either solution 1 or solution 2?

Solution 1:

class Point
 def initialize(x, y)
   @x = x
   @y = y
 end

 def distance
   Math.sqrt(@x ** 2 + @y ** 2)
 end
end

Solution 2:

class Point
  def initialize(x, y)
   @x = x
   @y = y
  end

  def distance
    Math.sqrt(x ** 2 + y ** 2)
  end

private 
  attr_reader :x, :y

end
Filip Bartuzi
  • 5,711
  • 7
  • 54
  • 102
  • 1
    There are times when you want to use "Solution 3": [Object#instance_variable_get](http://ruby-doc.org/core-2.2.0/Object.html#method-i-instance_variable_get). Suppose, for example, you wanted to compute the sum of squares of the values of all instance variables. You could write `def sum_of_squares; instance_variables.reduce(0) { |t,v| t + instance_variable_get(v) ** 2 }; end`, then `Point.new(3,5).sum_of_squares #=> 34`. [Object#instance_variables](http://ruby-doc.org/core-2.2.0/Object.html#method-i-instance_variables) returns an array of the instance variables. – Cary Swoveland Oct 31 '15 at 19:25
  • @CarySwoveland That's tricky, I like it! I believe this approach is perfect for http://codegolf.stackexchange.com/ :D – Filip Bartuzi Nov 01 '15 at 09:48
  • `attr_reader` is just a shortcut to avoid writing the same, simple and generic getter methods for every attribute. It's part of DRY, imo. – Charles Nov 01 '15 at 15:33
  • @c650 it's not "just" a shortcut. It's C extension which is few times faster than writing methods by hand. https://www.omniref.com/ruby/2.2.0/files/method.h?#annotation=4081781&line=47 – Filip Bartuzi Nov 01 '15 at 15:49
  • @FilipBartuzi so I've heard. To me, however, if you're using Ruby there's a chance that you don't care about the few extra seconds. Wouldn't you use a different language if you actually cared about speed? – Charles Nov 01 '15 at 18:44
  • @c650 Surprise, surprise but people actually do care about efficiency of their Ruby code. you can see community discussing it on [slides](https://speakerdeck.com/sferik/writing-fast-ruby) or [rails conference](https://www.youtube.com/watch?v=B3gYklsN9uc) – Filip Bartuzi Nov 01 '15 at 19:02

2 Answers2

4

I would use the second option:

class Point
  def initialize(x, y)
   @x = x
   @y = y
  end

  def distance
    Math.sqrt(x ** 2 + y ** 2)
  end

private 

  attr_reader :x, :y    
end

For two reasons:

  1. attr_reader might be faster (as Filip Bartuzi already pointed out)
  2. Using attr_reader might make it easier to refactor that class later on by replacing the attr_reader with a custom getter method.
spickermann
  • 100,941
  • 9
  • 101
  • 131
  • Good solution. I can think of a third reason. Have a look at the comment I left on the question. Another way of doing that is `def sum_of_squares; instance_variables.reduce(0) { |t,sym| t + send(sym.to_s[1..-1]) ** 2 }; end`, then `Point.new(3,5).sum_of_squares #=> 34`. For each instance variable, this sends the string representation of its getter to the instance to obtain the value of the instance variable. – Cary Swoveland Oct 31 '15 at 19:41
0

attr_reader :x, :y defines the following functions:

def x
  @x
end

def y
  @y
end

So both methods are equivalent and in fact identical. Performance is also similar in both cases, although attr_reader can be faster than some other variable accessing methods..

Community
  • 1
  • 1
Leo Brito
  • 2,053
  • 13
  • 20
  • attr_reader is 3x faster than define_method: https://www.omniref.com/ruby/2.2.0/files/method.h?#annotation=4081781&line=47 – Filip Bartuzi Oct 31 '15 at 13:10
  • @FilipBartuzi the question is asking about direct access, though, which is not mentioned in the article. – Leo Brito Oct 31 '15 at 13:16