下面程序码以图表方式呈现,可看出物件与类别之间的关联:
class MyClass
end
obj1 = MyClass.new
obj2 = MyClass.new
obj3 = MyClass.new obj3.instance_variable_set('@x', 10)
请先看以下程序码:
module Printable
def print
"Printable#print"
end
def prepare_cover
# ...
end
end
module Document
def print_to_screen
prepare_cover
format_for_screen
print
end
def format_for_screen
# ...
end
def print
"Document#print"
end
end
class Book
include Document
include Printable
# ...
end
当我们建立了 Book 的实例物件 b,并呼叫 print_to_screen()方法。
b = Book.new
b.print_to_screen
假设这个时候问题出现了,因为输出的字串是错误的,这代表呼叫了不对的 print()方法
首先我们可以请 Ruby 给点提示:
Book.ancestors # => [Book, Printable, Document, Object, Kernel, BasicObject]
从提示里,我们得知 ancestor's chain 正确的面貌。在问题中并没有特别说 Book 是继承自哪个类别,因此如果没有引入任何模组的话,在预设情况下,Book 是继承自 Object。
但是当 Book 用 include 引入了 Document 模组,Ruby 会把 Document 模组加在 Book 的上面,也就成为 Book 的父层。接着又再 include 了 Printable 模组,Ruby 会再把 Printable 模组插入在 Book 的上面。此时的 ancestor's chain 的图就会变成这样:
还记得 Chapter 2 介绍的 Method Lookup 吗?
当呼叫 b.print_to_screen 时,实例物件 b 会变成是当下执行的物件,也就是 self。接着依 『 黄金法则:往右ㄧ步,再往上 』 "One step to the right, then up",往右会先找到 Book 类别,再往上ㄧ直找到 Document#print_to_screen()。
进入 print_to_screen() 内,就准备开始执行里面的方法。不过 print_to_screen()里面的方法(包括 print)都没有明确的接收者(receiver),因此 method lookup 只能再次从 Book 往上找。以 print() 方法来说,最接近 Book 的 print() 方法会在 Printable 模组找到。
要解决掉 Bugs 可以有两种做法:
请找出以下程序码的错误并加以修正:
1 class Roulette
2 def method_missing(name, *args)
3 person = name.to_s.capitalize
4 3.times do
5 number = rand(10) + 1
6 puts "#{number}..."
7 end
8 "#{person} got a #{number}"
9 end
10 end
number_of = Roulette.new
puts number_of.bob
puts number_of.frank
正确的输出应该是这样:
# 5...
# 6...
# 10...
# Bob got a 10
# 7...
# 4...
# 3...
# Frank got a 3
如果我们直接拿以上错误程序码去跑跑看,会得到类似下面无限回圈的错误讯息 (SystemStackError)。
找出这个错误讯息应该不算是太难,但是要了解错误发生的原因就不是那麽浅显易见了。首先可以看出 number 变数是在 do...end 的 block 中定义的,然後会再传给 times() 方法。
还记得在 Chapter 4 中提到 Scope 的概念,在block内定义的变数,其作用域只限於 block 里。因此当程序跑到第8行的 number 时,Ruby 其实会认为 number 是方法,而不是在第 5 行所定义的变数。
假设我们在 Roulette 类别内定义的方法不是命名为 method_missing,其实错误讯息就是常见的名称错误(NameError),但题目的陷阱或是有趣的地方就在於方法名称是 method_missing。
我们知道当 Ruby 找不到方法或是无法回应当物件时,就会呼叫 BasicObject 所内建 method_missing() 方法,因此程序跑到第 8 行的 number 时,会错认为是方法,又在 self (Roulette的实例物件) 里找不到 number 方法,所以呼叫 method_missing(),才造成了无穷回圈的问题。
分享自己快速但觉得不是很好的解法:设定 number 为全域变数,程序就不会错认为第 8 行的 number 是方法了。
1 class Roulette
2 def method_missing(name, *args)
3 person = name.to_s.capitalize
4 3.times do
5 $number = rand(10) + 1
6 puts "#{$number}..."
7 end
8 "#{person} got a #{$number}"
9 end
10 end
书中提供的方法
1 class Roulette
2 def method_missing(name, *args)
3 person = name.to_s.capitalize
4 super unless %w[Bob Frank Bill].include? person
5
6 number = 0
7 3.times do
8 number = rand(10) + 1
9 puts "#{number}..."
10 end
11 "#{person} got a #{number}"
12 end
13 end
本篇所有题目都是来自於 Metaprogramming Ruby 2nd
手刻响应式网站,对我来说其实没有到很困难,但如果页面一多,时程又赶,就会很麻烦,而 Tailwin...
#布局 接续昨天的例子,我们如果新增一个Greeting("Jetpack Compose...
将各式 linter 与 formatter 工具整合於同个专案中,让开发者可以: 使用 Edito...
这是第一次参加铁人赛 原本是想要在转职期间来挑战写个 30天 的学习笔记 但在开赛前找到相关接案工作...
鬼故事 - 这东西真烂 Credit: Corentin Penloup 灵感来源:https://...