我正在嘗試進行以下作業,但顯然我遺漏了一些東西:
class Person
def fetch
puts 'Fetch it boy!'
end
def action(data)
data.call
end
end
class Animal
def play
Person.new.action(proc { fetch })
end
end
Animal.new.play # undefined local variable or method `fetch' for #<Animal:0x0000000001b172d0>
我的目標是Person執行Animal已選擇的方法。我也試過使用instance_exec但無濟于事。
現在我知道我可以使用eval并將方法名稱作為字串傳遞,但我想避免這種情況。有任何想法嗎?
提前致謝
免責宣告(各種):
請不要提出代碼重組或重寫。這個問題的目的是更好地理解塊在物件之間傳遞時的行為。
uj5u.com熱心網友回復:
TL; 博士
首先,您最初發布的代碼沒有定義物件之間的正確關系或協作,這是問題的一部分。其次,人們需要捕獲和轉發塊的方式通常相當不直觀,因此使用塊而不是簡單的依賴注入(例如用特定的 Animal 初始化一個新的 Person 來與之協作)或訊息傳遞的原因是這比它需要的要難一些。簡單通常更好,而且通常更容易在以后除錯和擴展。
也就是說,下面的重新設計在語意上更清晰,但也展示了如何在協作者之間將塊作為 Proc 物件轉發。Person 現在接受一個可選塊,當存在時將該塊作為 Proc 物件傳遞給 Animal 上的一個可以呼叫它的方法。
你的代碼重新設計
考慮以下重新設計,它努力將正確的行為附加到正確的物件上:
class Person
def speaks_to_animal species, phrase, &reaction
animal = Animal.new species
animal.reacts_to phrase, &reaction
end
end
class Animal
attr_reader :species
def initialize species
@species = species.to_s
end
def reacts_to phrase
case species
when 'lion'; pp "The #{species} eats you."
when 'dog'
block_given? ? yield : pp("The #{species} barks at you.")
else pp "The #{species} doesn't know what to do."
end
end
end
特別是,這里的目標是重新設計代碼,使 Person#speaks_to_animal 而 Animal#reacts_to_phrase 由一個人說話。這將保持行為所屬的位置,因為 Person 不 #fetch 并且 Animal 不應該知道任何有關 Person 物件內部的資訊來進行協作。
作為副產品,這種重新設計提供了更大的靈活性。你的塊現在是可選的,當它們被傳遞給一個 Person 時,它們然后被 Animal 轉發和呼叫,這似乎是你原始代碼的意圖。您現在可以通過 Person 與 Animal 互動,并且該 Person 可以與您選擇指定的任何型別的 Animal 物種交談,而無需對 Animal 進行子類化或對反應進行硬編碼。例如:
person = Person.new
person.speaks_to_animal :dog, "Fetch, boy!"
person.speaks_to_animal(:dog, "Fetch, boy!") do
pp "The dog brings the stick back to you."
end
person.speaks_to_animal :lion, "Fetch, boy!"
If you don't pass a block to person, then the dog doesn't know what to do, and just barks at you. If you pass a behavioral expectation as a block, that block gets forwarded to animal#reacts_to where it's called via yield. Of course, if you ask a lion to play fetch, bad things are going to happen.
Repackaging the behavior and relationship between the objects allows you to do all sorts of things, like keying off elements of the phrase spoken by person to enable more complex responses, or allowing animal to key off elements of the phrase to respond differently based on its species. Mostly, though, this new code solves the problem of how to pass an optional block representing the Animal's reaction without coupling the objects too tightly.
從語意上講,一個人應該知道他們在和什么動物說話,以及他們希望動物做些什么來回應。傳遞一個塊是否真的是代表 Person 的期望或 Animal 反應的最佳方式更具爭議性,我個人會選擇更多地關注基于物種和短語的定義反應,而不是傳遞 Proc 物件。你的旅費可能會改變。
uj5u.com熱心網友回復:
您正在尋找:
instance_eval(&data)
object.instance_eval評估塊,但self在該塊(通常是self在其中創建的背景關系塊)中替換為object:
whoami = proc { self }
whoami.call => main
1.instance_eval(&whoami) => 1
但是請注意,這instance_eval也會將物件作為引數傳遞給塊,如果您想傳遞 lambda,這可能會出現問題:
whoami = lambda { self }
1.instance_eval(&whoami) #=> ArgumentError (wrong number of arguments (given 1, expected 0))
還有一種類似的方法:instance_exec. 它不僅不將 self 作為引數傳遞:
whoami = lambda { self }
1.instance_exec(&whoami) #=> 1
但它還允許您傳遞其他引數:
add_number = lambda { |number| self number }
1.instance_exec(3, &add_number) #=> 4
自然,這兩種方法都需要格外小心且非常謹慎地使用 - 當您想以宣告方式宣告要在每個實體上執行的類范圍的鉤子時,它們是很好的。它們不應該被用作兩個物件之間互動的手段,除非你真的知道你在做什么并且可以證明它不會驗證封裝。
uj5u.com熱心網友回復:
這是使事情變得簡單的選項:
class Animal
def play
Person.new.fetch
end
end
如果您確實需要將方法名稱作為值傳遞,最自然的做法可能是使用符號(而不是字串):
class Person
def fetch
puts 'fetched'
end
def action(a)
self.send(a)
end
end
class Animal
def play
Person.new.action(:fetch)
end
end
Animal.new.play
或者你可以這樣做:
class Person
def fetch
puts 'fetched'
end
def action(a)
a.bind(self).call
end
end
class Animal
def play
fetch = Person.instance_method(:fetch)
Person.new.action(fetch)
end
end
uj5u.com熱心網友回復:
在我看來,這個組織是不正確的。
即使有這個解決方案,動物仍然在控制人,而我們通常假設人將啟動取物命令。
class Person
def initialize(name = 'Happy Pet Owner', pet = Animal.new)
@name = name
@pet = pet
end
def fetch
'Fetch it boy!'
end
def action(&block)
print "#{@name} says: ", fetch
block.call
end
end
class Animal
def play
Person.new.action { puts " Fido chases ball." }
end
end
Animal.new.play
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/358268.html
上一篇:為什么這個簡單的ruby??代碼在命令列中不起作用,但在將其粘貼到irb中時起作用
下一篇:如何將影像從固定大小影片到全屏?
