关于ruby:为什么在初始化时不使用setter方法?

Why are setter methods not used in initialization?

最近我一直在读"Ruby中面向对象的实用设计",我注意到其中一个最佳实践是使用访问器方法,而不是直接获取@instance_variable。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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

保持干燥是有意义的,如果我需要处理@bar,在实际获取它的价值之前。但是,我注意到initialize方法直接设置@bar实例变量的值:

1
2
3
4
5
6
class Foo
  attr_accessor :bar

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

有什么原因吗?不应该使用setter方法而不是直接使用=运算符来设置实例变量的值吗?


你说得对,这样做更有意义

1
2
3
4
5
6
7
class Foo
  attr_accessor :bar

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

关于您是否应该尊重对象内部的封装,参数是不同的,但是如果您相信这一点,那么,是的,您应该这样做。


实际上,setter可以在initialize中使用,与其他方法相同,但setter不能在没有接收器的情况下使用。

我认为你可以使用a_foo.bar=self.bar=,但没有接收器就不能使用bar=,因为在以后的情况下,bar将被视为局部变量而不是setter方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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

这将导致:

1
2
3
#<Song:0x23a50b8 @name="Mike">
#<Song:0x23a50b8 @name="John">
#<Song:0x23a50b8 @name="John">


初始值设定项在初始化时设置值。访问器允许您在对象已经实例化后通过符号访问(读/写)。

这篇文章可能有助于你理解:Ruby中的attr_accessor是什么?


虽然您可以在初始化中使用setter,如@uncutstone's answer所示,但您不能像在代码的注释中建议的那样使用它。

问题是Ruby会解释:

1
bar = my_argument

作为对bar局部变量的赋值,而不是对bar=方法的调用。

这在"Ruby setter为什么需要"self"中有相当广泛的讨论。类内的资格认证?".