Ruby:布尔属性命名规范和用法

33

我正在学习 Ruby。我的印象是布尔属性应该按照以下方式命名:

my_boolean_attribute?

然而,当我尝试进行以下操作时,我会收到语法错误:

class MyClass
  attr_accessor :my_boolean_attribute?

  def initialize
    :my_boolean_attribute? = false
  end
end

显然,Ruby不喜欢“?”。这是惯例吗?我做错了什么吗?

6个回答

46

编辑:三年后,时代已经发生了变化...

Julik的回答是当今解决这个问题最简单、最好的方法:

class Foo
  attr_accessor :dead
  alias_method :dead?, :dead # will pick up the reader method
end

以下是我对原问题的回答,为了后人留存...


简短版:

不能在实例变量名中使用问号。

详细版:

attr_accessor :foo为例——它在概念上仅仅是下面代码的一种语法糖:

def foo
  @foo
end

def foo=(newfoo)
  @foo = newfoo
end

此外,问号后缀通常只是一种惯例,用于表示方法的返回值是布尔值。

我能够做出的最好的近似是,你在这里所追求的是什么...

class MyClass

  def initialize
    @awesome = true
  end

  def awesome?
    @awesome
  end

end
在这种情况下,可以考虑使用 attr_accessor——毕竟,明确表明你直接使用布尔属性。通常,我会将问号后缀保存下来,当我实现一个基于略微复杂的条件而非仅仅属性值的布尔返回值时,再使用它。
干杯!
  1. Ruby强制执行某些命名约定。Ruby中的符号不能带有问号。因此,尝试调用 :my_boolean_attribute? 将导致 NameError 错误。编辑:不正确,只需为符号使用带引号的语法,例如::"my_attribute?"
  2. 符号是不可变的,尝试对其进行分配将抛出 SyntaxError

7
Ruby程序员通常在变量名和方法名中都不使用"is_"前缀。使用"Just awesome?"代替"is_awesome?",很棒吧? - Simone Carletti
很好的观点,weppos。编辑掉了“is_”以避免混淆后人;-) - Nick Zadrozny
从这个答案中我得出结论,没有真正的惯例? :/ - Steven Jeuris
2
你可以使用 :"my_boolean_attribute?",甚至是 :"my boolean attribute?"(不过如果没有使用 send 的话,后者可能会有些困难哦!) - Andrew Grimm
这不仅仅是语法糖,正如https://dev59.com/pW445IYBdhLWcg3wE2Xw#5806371中所提到的那样,还有性能上的收益。 - Andrew Grimm
1
对于您对编辑答案的追求严谨一点,符号可以有问号,但变量名(比如实例变量)不行。请参考@Julik的答案,这是我认为最好的做法。 - Excalibur

43

快速添加“问题方法”的最简单方法是使用你的读取器方法进行别名设置

class Foo
  attr_accessor :dead
  alias_method :dead?, :dead # will pick up the reader method
end 

太棒了!我一直在手动操作。 - Dane O'Connor
使用这个,您可以同时使用 deaddead?,对吧?但是只允许通过 dead 进行设置? - Steven Jeuris
3
设置保持不变直到"dead="。 - Julik

6
< p > attr_accessor 符号意味着变量名是 @my_boolean_attribute ,所以这就是您应该设置的内容(而不是符号)。

另外,您不能在变量中使用?,只能在方法名称中使用。


谢谢你捕捉到了这个符号。self.my_boolean_attribute可以正常工作吗? - Dane O'Connor
哦,让我补充一点——只限于实例方法。在类作用域中,它会查找类作用域变量(@@my_boolean_attribute)。当然,我并不需要告诉你,但我想确保我所说的在技术上是正确的。 - Platinum Azure

5

? 是方法名的惯例,而不是变量名。你不能使用名为@foo?的实例变量,但是如果你想的话,可以使用名为@foo的变量,并将(手动创建的)getter方法命名为foo?


哦,所以attr_accessor助手不会自动去掉结尾的'?',听到这个我很高兴,因为这实际上也不是实例变量的惯例。我还以为我疯了呢。 - Dane O'Connor

3

猴子补丁元编程 - 或许可以让其更加优雅,这只是一个快速草稿,而且我已经有一段时间没有进行元编程了...

 # inject the convenience method into the definition of the Object class
 class Object
   def Object::bool_attr(attrname)
     class_eval { define_method(attrname.to_s,
          lambda { instance_variable_get('@' + attrname.to_s.chop) }) }
     class_eval { define_method(attrname.to_s.chop+"=",
          lambda { |x| instance_variable_set('@'+attrname.to_s.chop, x) }) }
   end
 end

 ### somewhere later

 class MyClass

   bool_attr :my_boolean_attribute?

   def initialize
     @my_boolean_attribute = true
   end
 end

 # yet even more later

 foo = MyClass.new
 bar = MyClass.new

 foo.my_boolean_attribute = 1
 puts foo.my_boolean_attribute?
 puts bar.my_boolean_attribute?

采用这种方法,你可以做到DRY并获得漂亮的问号。你只需要选择一个更好的名称,比如“bool_attr_accessor”或类似的名称。
我所定义的内容有点古怪,因为原始符号中包含了问号。可能更清晰的方法是避免在符号名称中使用问号,并在方法定义期间添加它-应该不会引起混淆。
哦,几乎忘记包括必要的链接:Seeing metaclasses clearly

+1 给链接!在这种情况下可能不会添加方便的方法,因为这不是惯例。我不想让其他人感到困惑。但是这些想法将被其他问题愉快地吸收! - Dane O'Connor
1
如果你在团队中维护它,我猜这种问题应该在审查时进行深入讨论——在某些情况下可能是有意义的,在某些情况下则不是。我没有在团队中编写过Ruby代码,所以我倾向于采用一种方法,使我需要盯着最少的LOC。 - Andrew Y

0

我浏览了答案,尽管被接受的回答很到位,但它在类中引入了“额外”的噪音。我建议解决这个问题的方式是:

class Animal
  attr_writer :can_swim
  def initialize(animal_type_name)
    @can_swim = true
    @animal_type_name = animal_type_name
  end


  def can_swim?
    @can_swim
  end

  def to_s
    @animal_type_name
  end
end

dog = Animal.new('Dog in a bag')
dog.can_swim = false
puts "Can this #{dog} Swim? --- [#{dog_without_legs.can_swim? ? 'YEP!' : 'NOPE!'}]"

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接