簡體   English   中英

什么時候應該使用別名方法? -紅寶石

[英]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核心庫中的一些示例是收集方法,其名稱對於來自功能性編程語言(例如mapreduce )的人來說是熟悉的名稱,而屬於Smalltalk編程語言家族的人(例如collectinjectselect ,以及標准的英文名稱,例如find_all 。)另一種可能性是,您構建一個Fluent接口並希望它能更流暢地閱讀,如下所示:

play this
and that
and something_else

在這種情況下, and可能是一個別名play

另一個相關原因(我們將其稱為原因1.5)是您要實現某種協議,並且已經有一種方法可以正確實現該協議的語義,但是名稱錯誤。 讓我們假設一個假設集合框架,該框架具有兩個方法map_seqmap_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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM