Why do we need attr

I fail to understand why we needed the attr_reader and attr_writer, or attr_accessor to be declared in a class. I read both this and this posts, but those posts explain mainly how they work, not as much as why they are there.

In case of

class Person
  attr_accessor :age
end

bob = Person.new
bob.age = 99
bob.age

It seems a little redundant having to tell Ruby to write and read age, while not being able to write and read instance variable outside the class automatically. Why do we need to set up reader and writer in the Class instead of the following code and save a few lines?

class Person
end

bob = Person.new
bob.age = 99
bob.age 

Because it makes the code easier to maintain.

This way its clear from reading the class code what you expect to have happen, and that you expect other classes to access these variables.

Without this outside code could simply access your internal variables, and then if you refactor your code and remove or rename such a variable the outside code breaks.

Thus the accessor methods make it clear you intend for other classes to access these methods

FYI: Ruby is very powerful, and if you want you can go ahead and make it work the way you want. I don't recommend this but am pointing it out so you will understand that its a explicit choice being made for the good of keeping your code readable. But if you want to see how you could do this read on, and try running the code...

<script type="text/ruby">

def puts(s)  # ignore this method, just dumps to the display
  Element['#output'].html = Element['#output'].html + s.to_s + "<br/>"
end

class OpenKimono
  def method_missing(name, *args)
    if name =~ /=$/
      instance_variable_set("@#{name[0..-2]}".to_sym, args[0])
    else
      instance_variable_get("@#{name}")
    end
  end
end

class MyClass < OpenKimono
end

foo = MyClass.new

foo.bar = 12

puts "foo.bar just set to 12, it now = #{foo.bar}"

foo.baz = 13

puts "foo.baz just set to 13, it now = #{foo.baz}"

puts "foo.manchu has never been set... what does it equal? #{foo.manchu}"

</script>


<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://rawgit.com/reactive-ruby/inline-reactive-ruby/master/inline-reactive-ruby.js"></script>
<div id="output" style="font-family: courier"></div>

OpenStruct behaves like OP's second snippet:

require 'ostruct'

bob = OpenStruct.new
bob.age = 99
p bob.age # => 99

So there is a choice.


In many OO languages you have a single table shared between the object's methods and the object's properties. In Ruby, all the object's consumer should be concerned with are the object's methods. If you have this in JS:

Tank:
   weight: 123
   moveForward: function()...

in Ruby you would have:

Tank:
  ivars:
     @weight = 123
  methods:
     weight: return @weight # or other implementation, or a proxy...
     move_forward...

The reason for having two distinct tables is allowing the object to decide how to store data and whether to store it, instead of having it's properties poked from outside. This allows for one extremely important advantage: UAP is maintained. That is, from the outside you work with the object's methods (sending it messages) regardless of whether those are properties or methods.

It is a wonderful feature that many other object-oriented languages either disregard in the name of optimisation (avoid indirection) or by negligence/ignorance (python is a prime example). I've written a little article on the subject here: http://live.julik.nl/2012/08/messages-versus-slots

To answer the last part of your question:

Why do we need to set up reader and writer in the Class instead of the following code and save a few lines?

To prevent situations that you have all the time in JavaScript where you address a method or a property on an object only to find undefined in it's place, which will blow up your application many lines (or executio seconds) out of where you access the property.

In Ruby you will get an exception if you do, and it will save you from yourself thousands of times, trust me. As a matter of fact, you can create an object that will accept any getter/setter messages, and such object already exists - it's an OpenStruct. It's use normally not a good practice because of speed concerns.

[1] pry(main)> require 'ostruct'
=> true
[2] pry(main)> s = OpenStruct.new
=> #<OpenStruct>
[3] pry(main)> s.foo = 1
=> 1
[4] pry(main)> s.bar = 2
=> 2
[5] pry(main)> s.foo
=> 1
[6] pry(main)> s.bar
=> 2
[7] pry(main)> s.x
=> nil

An additional benefit from using the attribute method generators is that most Ruby documentation engines (both RDoc and YARD) will look for those to document your object methods quicker.

链接地址: http://www.djcxy.com/p/25732.html

上一篇: Akka:在演员系统之外进行沟通?

下一篇: 为什么我们需要attr