面向对象编程 ( OOP ) 中,「 成员访问控制 」 ( member access control
)是一个非常重要的概念,因为它可以用来控制对封装变量的访问。
大多数编程语言中,这个概念是通过为每个成员添加 getter
和 setter
来实现的,但在 Ruby 中,是通过 attr_*
方法来实现成员访问控制的。
attr 方法
当把一个属性名当作参数传递给 attr
时, attr
方法会该属性创建一个实例变量和一个 getter
方法
attr
方法的参数必须是一个 Symbol
或者一个可以转换为 Symbol
的字符串。
module Attr attr :attr1, 'attr2' end irb> Attr.instance_methods => [:attr1, :attr2]
上面这段代码,Attr
模块包含了用于读取 attr1
和 attr2
值的两个实例方法
attr_reader 方法
attr_reader
方法和 attr
方法类似
module Attr attr_reader :attr1, 'attr2' end irb> Attr.instance_methods => [:attr1, :attr2]
attr_writer 方法
attr_writer
方法会为传递的每个参数创建一个实例变量和一个 setter
方法。
传递的参数必须是一个属性名。且必须是一个 Symbol
或可以转换为 Symbol
的字符串。
module Attr attr_writer :attr1, 'attr2' end irb> Attr.instance_methods => [:attr1=, :attr2=]
上面的代码中,Attr
模块包含了两个实例方法分别用于修改 attr1
和 attr2
属性。
attr_accessor 方法
attr_accessor
是 attr_reader
和 attr_writer
的结合体,会为传递的参数创建一个实例变量和一个 getter
方法和一个 setter
方法
同样的,传递的参数必须是一个属性名。且必须是一个 Symbol
或可以转换为 Symbol
的字符串。
module Attr attr_accessor :attr1, 'attr2' end irb> Attr.instance_methods.sort => [:attr1, :attr1=, :attr2, :attr2=]
上面的代码中,Attr
模块包含了四个实例方法,分别用于读取和设置实例属性 attr1
和 attr2
懒加载 ( 延迟初始化 )
前面的示例中,我们讲解的都是如何使用 attr_ *
方法创建实例变量。但幕后,attr_ *
方法创建的实例变量的时候使用了 「 延迟初始化 」 ( lazy Initialization ) 的机制
这意味着实例变量仅在调用 setter 方法时初始化 - 或者在实例方法中显式设置实例变量时才初始化
class Website attr_accessor :link, :title def initialize @title = "The best Ruby newsletter" end end irb> website = Website.new => #<Website:0x00777 @title="The best Ruby newsletter"> irb> website.instance_variables => [:@title] irb> website.link = "http://ruby.devscoop.fr" => "http://ruby.devscoop.fr" irb> website.instance_variables [:@title, :@link]
上面的示例中,@sitl
实例变量是在 Website#initialize
方法中显式创建的。因此,此实例变量在第一次调用 instance_variables
返回的数组中才是可用
请注意,尽管调用了 attr_accessor
方法,但尚未创建 @link
实例变量。此实例变量仅在调用 Website#link=
方法后才初始化和存在
因此,attr_*
方法的这种懒加载机制,只有在我们为其设置值时才会创建与每个参数名称关联的实例变量(通过使用 setter
方法或通过显式赋值给它)