Baurine's Blog

Ruby 属性访问器

April 12, 2017

Ruby 的类的成员变量的定义方式是 @variable,但是在类外部,我们无法通过 instance.@variable 的方式访问它。这种机制决定了 ruby 的类成员变量只能是私有的,如果想从外部访问它,就必须为它定义公开的方法,或者使用属性访问器。

属性访问器的原理是自动为这些成员变量生成公开方法。所以可见,对于 ruby 来说,对外公开的只有方法,没有属性。这个特性好啊,相比 Java 这种语言,在实现 Model 时,就会很犯难,到底是直接把属性都 public 了,还是 private + getter / setter,后者实在是啰嗦。

1. 手动定义方法

class Foo
  def initialize
      @valid = false
  end

  def valid?
      @valid
  end
end

2. 使用属性访问器

attr_reader / attr_writer / attr_accessor

class Foo
  attr_reader :name
  attr_writer :age
  attr_accessor :gender

  attr_reader :valid
  alias valid? valid

  def initialize(name)
    @name = name
    @valid = false
  end
end

attr_reader 定义的成员变量,可以在外部被读取,但不能在外部被修改,只能在内部被修改。attr_reader :name 等价于:

def name
  @name
end

attr_reader 定义的成员变量在内部的使用方法:

  • 读取

    def test
      # 两者效果相同
      puts @name
      puts name  # name 是方法
    end
  • 修改

    @name = 'bar'

如果在内部修改时没有加上 @,那么实际上创建了新的局部变量:

def test
  puts name    # 此时 name 是方法,将输出 @name 的值
  name = 'bar' # 创建了新的局部变量 name,值为 bar
  puts name    # 输出 bar
end

在外部访问的方式:

f = Foo.new
puts f.name

f.name = 'foo' # ERROR! 不能被修改,不存在 f.name= 方法

attr_writer 定义的成员变量,可以在外部被修改,但不能在外部被读取。attr_writer :age 等价于:

def age=(val)
  @age = val
end

在外部被修改:

f = Foo.new
f.age = 20

puts f.age # ERROR! 不能被访问,不在存 f.age 方法

使用 attr_writer 定义的成员变量,在内部被修改时,有 2 种使用方式:

@age = 40
# or
self.age = 40

# 创建了新的局部变量
age = 40

attr_accessor 定义的成员变量,既可以在外部被读取,也可以被修改。attr_accessor :gender 等价于:

def gender
  @gender
end

def gender=(val)
  @gender = val
end

(或许你还会有这样的疑问,什么情况下我会让一个属性可以在外部被修改,却不能被读取,就像是文件的只写属性,这样的场景确实很少,但的确有,最常见的就是程序的日志功能,当你的 Web 程序运行的时候,你只会去写日志,但永远不会去读日志。而另外一个分析日志的程序,只用来读日志,却不会去改变日志。)

对于 boolean 型的成员变量,我们习惯于在它的访问方法名中加上 ?,所以我们可以用 alias 关键字来定义一个别名。

attr_reader :valid
alias valid? valid

使用:

f = Foo.new
puts f.valid?

Comments