我应该使用什么约定的实例变量?

时间:2011-09-26 12:32:54

标签: ruby

Ruby代码中是否使用了任何实例变量约定?也就是说,我注意到,在示例部分中,使用'@'初始化实例变量,然后代码使用不带'@'的变量名称:

class BookInStock
  attr_reader: isbn
  attr_accessor: price

  def initialize (isbn, price)
    @isbn = isbn
    @price = Float (price)
  end

  def price_in_cents
    Integer (price * 100 + 0.5)
  end
end

还有一些例子,代码,实例变量一直使用前缀'@'

class BookStock

  def set_price (price)
    @price = price
  end

  def get_price
    @price
  end
end

这些记录有什么区别?什么时候我应该只在对象的初始化和所有类方法中使用'@'?

3 个答案:

答案 0 :(得分:6)

在第一个示例中,@price是一个实例变量。

price_in_cents的方法定义中,price实际上是对price()的方法调用。由于Ruby的语法糖,()被省略。

看起来price()没有明确的定义,但attr_accessor :price定义了price()price=(value) a.k.a. getter和setter。您可以尝试注释掉attr_accessor :price行,当BookInStock的实例调用price_in_cents时,您将获得“未定义的局部变量或方法”异常。

实例变量始终具有@前缀。如果未定义访问者方法,则不能使用不带@前缀的名称。

答案 1 :(得分:6)

如果您为此变量定义了属性读取器(@attr_reader),则只能使用不带attr_accessor的实例变量名称。这是一个轻量级的帮助器,用于创建一个返回实例变量值的方法。

区别在于使用班级的公共界面或访问私人数据之间的问题。例如,建议在子类或包含的模块中使用公共接口。这确保了封装。所以你不应该从子类或模块中访问实例变量。

在类本身中,问问自己这个问题:如果属性的实现发生了变化,我的使用属性是否也会发生变化?如果答案是,请直接使用实例变量;如果,请使用公共属性。

一个例子。假设我们有一个代表一个人的Person类,并且应该包含该人的名字。首先是这个版本,只有一个简单的name属性:

class Person
  attr_reader :name

  def initialize(name)
    @name = name
  end

  def blank?
    !@name
  end

  def to_s
    name
  end
end

请注意我们如何对blank?方法使用实例变量,而对to_s方法使用公共属性。这是故意的!

我们假设在重构步骤中我们决定Person类应单独跟踪first_namelast_name

class Person
  def initialize(first_name, last_name)
    @first_name, @last_name = first_name, last_name
  end

  def name
    "#{@first_name} #{@last_name}"
  end

  def blank?
    !@first_name and !@last_name
  end

  def to_s
    name
  end
end

我们现在将name属性更改为常规方法,该方法根据人名和姓氏组成名称。我们现在可以看到,to_s可以保持相同的实现。但是,blank?需要更改,因为它依赖于基础数据。这就是to_s使用属性的原因,但blank?使用实例变量

当然,区别并不容易。如有疑问,请选择公共API而不是访问私人数据。

答案 2 :(得分:1)

使用name = value是一个错误,因为它会创建一个名为name的局部变量。您必须使用self.name = value

至于约定,如果你可以保证访问器总是轻量级attr_accessors,你只能使用@name。在所有其他情况下,使用@name而不是self.name会违反封装并让自己头疼。您在问题中给出了确切的原因 - 如果getter / setter中有额外的逻辑,如果直接访问实例变量,则必须复制它。