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
这些记录有什么区别?什么时候我应该只在对象的初始化和所有类方法中使用'@'?
答案 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_name
和last_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中有额外的逻辑,如果直接访问实例变量,则必须复制它。