[英]When should I use an Alias Method? - Ruby
我已瀏覽但未找到以下答案:
您將使用什么別名方法?
class Vampire
attr_reader :name, :thirsty
alias_method :thirsty?, :thirsty
end
我會使用的唯一原因是能夠使用我定義的任何方法使用問號嗎? 我相信您不能在實例變量中使用問號。
我認為這是我之前回答的一個問題,我在這里提出了使用alias_method
建議,因此我對此有一些額外的上下文來解釋它在該上下文中的使用。
在代碼段中,您有一些讀取attr_reader :thirsty
的代碼,該代碼基本上是同名實例變量( @thirsty
)的獲取器
def thirsty
@thirsty
end
在原始代碼段中,您有一個斷言:
refute vampire.thirsty?
您是否也有僅出於thirsty?
返回true
代碼thirsty?
方法,您的斷言失敗了。
至少有兩種方法可以修改代碼, thirsty?
工作,您的主張通過了:
創建一個調用thirsty
閱讀器的方法,或訪問@thirsty
實例變量本身:
def thirsty?
thirsty # or @thirsty
end
另一種方法是使用alias_method
,其功能與上述功能等效。 別名thirsty?
到thirsty
,這是一個attr_reader
,它從@thirsty
實例變量讀取
參考我給的另一個答案
您最好根本不使用attr_reader,而只是按照Sergio在他的評論中指出的那樣做:
class Vampire
def initialize(name)
@name = name
@thirsty = true
end
def thirsty?
@thirsty
end
def drink
@thirsty = false
end
end
為什么要使用Module#alias_method
原因有兩個,一個是當前有效的,另一個是過時的,無論如何實際上都沒有必要。
第一個原因是,您只想擁有兩個名稱完全不同的方法,它們可以執行完全相同的操作。 這樣做的一個原因可能是同一動作有兩個同樣廣泛使用的術語,並且您想使人們更容易編寫其社區可以理解的代碼。 (Ruby核心庫中的一些示例是收集方法,其名稱對於來自功能性編程語言(例如map
, reduce
)的人來說是熟悉的名稱,而屬於Smalltalk編程語言家族的人(例如collect
, inject
, select
,以及標准的英文名稱,例如find_all
。)另一種可能性是,您構建一個Fluent接口並希望它能更流暢地閱讀,如下所示:
play this
and that
and something_else
在這種情況下, and
可能是一個別名play
。
另一個相關原因(我們將其稱為原因1.5)是您要實現某種協議,並且已經有一種方法可以正確實現該協議的語義,但是名稱錯誤。 讓我們假設一個假設集合框架,該框架具有兩個方法map_seq
和map_nonseq
。 第一個執行map
並保證副作用的順序,而第二個則不保證副作用的順序,甚至可以異步,同時或並行執行映射操作。 這兩種方法實際上具有不同的語義,但是如果您的數據結構不適合並行化,則可以簡單地實現map_seq
並將map_nonseq
別名。
在這種情況下,驅動程序不是您要為同一操作提供兩個名稱,而是要為兩個名稱提供相同的實現(如果該語句有意義:-D)。
過去使用alias_method
的第二個主要原因與它的語義上的一個重要細節有關:當您覆蓋或使用monkey-patch這兩種方法中的任何一個時,這只會影響該名稱,而不會影響另一個。 過去曾用它來包裝方法的行為,如下所示:
class SomeClass
def some_method
"Hello World"
end
end
這有點無聊。 我們希望我們的方法大聲疾呼! 但是,我們不想只復制並重新實現該方法,我們想重新使用它的內部實現,而不必知道它是如何在內部實現的。 而且我們想猴子修補它,以便該方法的所有客戶端都具有喊叫行為。 流行的方法是這樣的:
class SomeClass
alias_method :__some_method_without_shouting :some_method
def __some_method_with_shouting
__some_method_without_shouting.upcase
end
alias_method :some_method :__some_method_with_shouting
end
在此示例中,我們使用alias_method
為正在進行猴子修補的方法創建一個“備份”,以便我們可以從該方法的猴子修補版本中調用它。 (否則該方法將消失。)這實際上是alias_method
文檔中給出的用例。
這個習慣用法如此流行並且被廣泛使用,以至於一些庫甚至為其提供了一個實現,例如ActiveSupport的Module#alias_method_chain
。
但是請注意,這種習語具有一些不太好的特性。 一種是我們使用所有_with_
和_without_
方法污染名稱空間。 例如,當您查看對象的方法時,將看到所有這些方法。 另一個問題是,人們仍然可以直接調用舊方法,但是大概我們有理由對其進行修補,因為我們不希望舊行為。 (否則,我們可以使用新名稱創建一個方法來調用舊方法,例如shout
。)
一直存在一種未被廣泛使用的更好的替代方法:
class SomeClass
some_method_without_shouting = instance_method(:some_method)
define_method(:some_method) do
some_method_without_shouting.bind(self).().upcase
end
end
在這里,我們將舊方法存儲在局部變量中,並使用一個塊定義新方法(通過Module#define_method
)。 局部變量在類主體的末尾超出范圍,因此永遠無法再訪問它。 但是塊是閉包 ,它們關閉了周圍的詞法環境,因此傳遞給define_method
的塊( 僅此塊)仍然可以訪問變量綁定。 這樣,舊的實現就被完全隱藏了,並且沒有名稱空間污染。
但是,從Ruby 2.0開始,此方法包裝有一個更好的解決方案: Module#prepend
。 prepend
在於它是“正義的繼承”,我們可以簡單地使用super
:
module Shouter
def some_method
super.upcase
end
end
class SomeClass
prepend Shouter
end
Module#prepend
是在ActiveSupport 5.0中不推薦使用Module#alias_method_chain
並在5.1中將其刪除的原因。 不再需要所有這些扭曲。
因此,總結一下:使用alias_method
主要原因有兩個:從字面上做一個別名,即同一操作的兩個名稱,以及為方法包裝創建備份副本。 第二個不再有效,也許可以說從來沒有。 今天,只有第一個原因是使用alias_method
的有效理由。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.