受保护的方法
本课属于 Coddy Ruby 学习旅程的“面向对象编程”单元。第 18 课,共 57 课。
受保护方法介于公共方法和私有方法之间。外部代码不能调用它,但同一个类的任何对象都可以在另一个对象上调用它。这样,两个对象就能比较那些不会向程序其他部分公开的数据:
class Account
def initialize(owner, balance)
@owner = owner
@balance = balance
end
def richer_than?(other)
balance > other.balance
end
protected
def balance
@balance
end
end
ada = Account.new("Ada", 500)
bo = Account.new("Bo", 300)
puts ada.richer_than?(bo)
begin
ada.balance
rescue NoMethodError => e
puts e.message
end输出:
true
protected method `balance' called for an instance of Account与 private 的区别会在接收者是另一个对象时显现出来。对于受保护的方法,other.balance 可以正常使用;对于私有方法则会失败:
class Sample
def initialize(n)
@n = n
end
def compare_protected(other)
other.guarded + guarded
end
def compare_private(other)
other.hidden + hidden
rescue NoMethodError
"private refused other.hidden"
end
protected
def guarded
@n
end
private
def hidden
@n
end
end
a = Sample.new(1)
b = Sample.new(2)
puts a.compare_protected(b)
puts a.compare_private(b)输出:
3
private refused other.hiddenprotected 也适用于 attr_reader,这是只在同一个类的对象之间共享值的常用方式,例如用来判断两个对象中谁会获胜:
class Player
attr_reader :name
def initialize(name, rating)
@name = name
@rating = rating
end
def stronger?(other)
rating > other.rating
end
protected
attr_reader :rating
end
a = Player.new("Ada", 1200)
b = Player.new("Bo", 1100)
puts a.stronger?(b)
puts [a, b].select { |pl| pl.stronger?(b) }.map(&:name).inspect输出:
true
["Ada"]选择合适的最窄访问级别。public 用于类的接口,protected 用于该类的对象彼此比较的数据,private 用于其他所有内容:
public # 任何人都可以访问:order.total
protected # 类的对象可以访问:在 Account 内使用 other.balance
private # 只有对象自身可以访问:subtotal、self.subtotal挑战
简单完成 Account 类。只有其他账户可以读取余额:将它的读取器设为受保护。richer_than?(other) 比较余额,transfer_to(other, amount) 通过另一个账户的受保护方法 receive(amount) 将钱转到另一个账户,并返回 true;如果余额不足,则返回 false。to_s 只返回账户所有者的姓名。
提供的代码会读取两个账户所有者的姓名和余额,然后读取 transfer a b 50 命令,并打印 ok 或 refused。接着,它会打印 Ada is richer(或 Bo is richer,或 equal),最后尝试从外部读取 balance,并在 Ruby 拒绝时打印 balance is protected。
你的代码写在 account.rb 中。main.rb 包含提供的输入/输出代码,不能编辑。
亲手试试
require_relative 'account'
# 提供的输入输出代码:保持原样。
input = []
while (line = gets)
input << line.chomp
end
accounts = input[0..1].map do |line|
owner, balance = line.split
Account.new(owner, balance.to_i)
end
a, b = accounts
input.drop(2).each do |line|
_, from, to, amount = line.split
source = from == "a" ? a : b
target = to == "a" ? a : b
puts source.transfer_to(target, amount.to_i) ? "ok" : "refused"
end
if a.richer_than?(b) then puts "#{a} is richer"
elsif b.richer_than?(a) then puts "#{b} is richer"
else puts "equal"
end
begin
a.balance
puts "balance is public"
rescue NoMethodError
puts "balance is protected"
end
这节课包含一个小测验。开始学习即可作答,并记录你的进度。
面向对象编程中的所有课
自己练习: Ruby 在线编译器