私有设置方法
本课属于 Coddy Ruby 学习旅程的“面向对象编程”单元。第 19 课,共 57 课。
通常,某个值应该对所有人都可读,但只有对象才能修改。attr_reader 提供公共读取器,而 private attr_writer 提供只有对象才能使用的设置器。这是因为 attr_writer 会返回它定义的方法名称:
class Wallet
attr_reader :balance
def initialize
self.balance = 0
end
def earn(amount)
self.balance = balance + amount
end
private attr_writer :balance
end
w = Wallet.new
w.earn(30)
puts w.balance
begin
w.balance = 1_000_000
rescue NoMethodError => e
puts e.message
end输出:
30
private method `balance=' called for an instance of Wallet私有 setter 在类内部通过 self. 调用,这是 Ruby 允许私有方法使用的唯一接收者。如果没有 self.,赋值只会创建一个局部变量:
class Lamp
attr_reader :on
def initialize
self.on = false
end
def toggle
self.on = !on
self
end
private
attr_writer :on
end
puts Lamp.new.toggle.toggle.toggle.on输出:
true通过私有 setter 进行赋值,而不是直接给 @balance 赋值,可以将规则集中在一处,例如检查值是否有效:
class Tank
attr_reader :litres
def initialize(capacity)
@capacity = capacity
self.litres = 0
end
def fill(amount)
self.litres = litres + amount
end
def drain(amount)
self.litres = litres - amount
end
private
def litres=(value)
@litres = value.clamp(0, @capacity)
end
end
t = Tank.new(50)
t.fill(80)
puts t.litres
t.drain(70)
puts t.litres输出:
50
0private attr_accessor :history 会隐藏读取器和设置器,用于存储外部代码甚至不应看到的内部状态:
class Editor
def initialize
self.history = []
end
def type(text)
history << text
end
private attr_accessor :history
end挑战
简单完成 Wallet 类。balance 可以从外部读取,但只能通过私有 setter 设置,初始值为 0。earn(amount) 会增加它的值。spend(amount) 会扣除相应的值并返回 true;如果余额不足,则不做任何更改并返回 false。history 返回成功更改的次数,也通过私有 setter 设置。
提供的代码会读取 +n 或 -n 命令,每次操作后打印 balance 30,如果扣款失败则打印 declined。然后,它会打印 changes: 2 并尝试执行 wallet.balance = 1000000,当 Ruby 拒绝此操作时打印 cannot set balance。
你的代码要写在 wallet.rb 中。main.rb 包含提供的输入/输出代码,不能修改。
亲手试试
require_relative 'wallet'
# 已提供的输入输出代码:保持原样。
input = []
while (line = gets)
input << line.chomp
end
wallet = Wallet.new
input.each do |cmd|
amount = cmd[1..].to_i
if cmd.start_with?("+")
wallet.earn(amount)
puts "balance #{wallet.balance}"
elsif wallet.spend(amount)
puts "balance #{wallet.balance}"
else
puts "declined"
end
end
puts "changes: #{wallet.history}"
begin
wallet.balance = 1_000_000
puts "balance set"
rescue NoMethodError
puts "cannot set balance"
end
这节课包含一个小测验。开始学习即可作答,并记录你的进度。
面向对象编程中的所有课
自己练习: Ruby 在线编译器