溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Ruby2.1中Refinements特性有哪些

發(fā)布時間:2022-01-14 14:44:59 來源:億速云 閱讀:105 作者:iii 欄目:大數(shù)據(jù)

這篇文章主要介紹“Ruby2.1中Refinements特性有哪些”,在日常操作中,相信很多人在Ruby2.1中Refinements特性有哪些問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”Ruby2.1中Refinements特性有哪些”的疑惑有所幫助!接下來,請跟著小編一起來學(xué)習(xí)吧!

Monkey Patches(猴子補?。┮恢笔荝uby Open Class(開放類)特性的副作用, 不過也有人把Monkey Patches看作是Ruby的一種特性。為什么叫「猴子補丁」呢? 進化未完全唄,這也是Ruby社區(qū)中傳出來的名字,不管如何,這個命名代表著貶義,意味著危險。

Ruby的開放類給開發(fā)者以很大的自由與靈活,但是,當(dāng)你打開一個類,添加自己的方法的時候,你有沒有想過這個方法會覆蓋掉已有的方法,自己寫的代碼還好,但是你如果用Ruby內(nèi)建的類,或者是第三方gem提供的類,Monkey Patches隨時有可能讓你的代碼穿越。

有一個比較典型的案例,早年的Ruby1.8.7 preview版本之前,是沒有Symbol#to_proc這個方法的,但是Rails自己通過Monkey Patch實現(xiàn)了Symbol#to_proc方法,結(jié)果Ruby1.8.7preview版本之后,添加了Symbol#to_proc方法,導(dǎo)致了Rails出現(xiàn)了一些不對勁的問題。

在Refinements出現(xiàn)之前,Ruby社區(qū)避免Monkey Patches的方法基本有以下幾種:

  • 使用別名/ alias_method_chain

  • 使用module 加命名空間

  • 強制檢查要添加的方法是否被定義

Ruby社區(qū)里討論了好多年的Refinements特性,就是為了解決上述問題,讓你安全的使用開放類。直到Ruby2.0才加進來這個特性,但是屬于實驗性的,不建議使用,但是前兩天隨著Ruby2.1穩(wěn)定版的發(fā)布,Refinements特性解除了實驗狀態(tài),意味著Ruby團隊支持建議你去使用Refinements特性了,但是比較悲劇的是, Refinements的文檔沒有跟上,你要去看文檔學(xué)習(xí)這個新特性的話,多半會出錯。

下面我總結(jié)了一下Ruby2.1中Refinements的大致用法,如有遺漏,請告訴我。

一、普通青年使用Refinements的方式:

 
# refinements提供一種方法,讓類的修改只影響到某個作用域

#判斷一個字符串是不是數(shù)字型字符串
module NumberQuery
  refine String do
    def number?
      !!match(/^[0-9]+$/)
    end
  end
end

# 并不是定義了就能用的
class A
  def a(n)
    n.respond_to?(:number?)
  end
end

A.new.a "123"  #=> 這里會返回false,意味著沒有定義number?方法

# 你必須用Module#using 方法
class A
  using NumberQuery 
end

# 你以為打開A類,using NumberQuery就可以了? 你太天真了。
A.new.a “123”  #=> false

# 看清楚,你必須重新定義A#a方法
class A
  using NumberQuery
  def a(n)
    n.respond_to?(:number?)  #=> true
  end
end

A.new.a "123" #=> 返回true,證明number?方法可以用了。

可見, 你使用了Module#refine方法打開類去增加的方法,只能使用Module#using方法在需要使用這個補丁的地方,引入補丁模塊,才可以使用。而且,注意上面的示例代碼,你必須在類定義的原始地方去using NumberQuery才起作用。這有點類似java或.net中的概念,Classboxes,即classbox的修改只對本classbox(或者導(dǎo)入它的 classbox)是可見的,這個特性我們稱之為本地重綁定(local rebinding)。 C#里ms也有一個using,用于擴展方法, C#的擴展方法僅僅在其顯式導(dǎo)入的代碼中才是可見的, 這也和我們上面的Ruby示例相似。所以Ruby中的Refinements算是改進版的Classboxes了。

二、二逼青年使用Refinements的方式

 
module NumberQuery
  refine String do
    def number?
      !!match(/^[0-9]+$/)
    end
  end
end

然后:

 
class String
  using NumberQuery
  def other_method
    puts "hello" if number?
  end
end

當(dāng)然,這不會造成String類全局的污染,只限于other_method方法,但是,請注意,你是不是有慣性的打開類進行Monkey Patches了?

有人可能這么用:

 
class T
  refine String do
    def number?
      !!match(/^[0-9]+$/)
    end
  end
end

哥,拜托,這樣是會報錯的:

 
  NoMethodError: undefined method `refine' for T:Class

這意味著,你不能在一個類中去使用refine。

還有人有點小聰明,他這么用:

 
# 我這個模塊,不僅僅是打補丁的啊,還有其他方法
module NumberQuery
  refine String do
    def number?
      !!match(/^[0-9]+$/)
    end
  end
 
  def hello
    puts "world".number?
  end
end

# 那么我在class A中,除了using,還得include
class A
  using NumberQuery
  include NumberQuery
end

這樣是行不通的, 還是去學(xué)學(xué)普通青年的用法吧,NumberQuery#hello方法中使用的number?是不合法的。

還有人在想,這多麻煩啊,還得寫兩遍模塊的名字,有了,我用included方法:

 
module NumberQuery
  def self.included(base)
    base.send(:using, self)
  end
 
  refine String do
    def number?
      !!match(/^[0-9]+$/)
    end
  end
 
  def hello
    puts "world"
  end
end

# 這樣,我就可以只include一次NumberQuery模塊了。
class A
  include NumberQuery
end

打住吧,兄弟,你又犯二了,睜大眼睛看看報的什么錯吧!

有位兄弟,想定義個類方法:

 
module NumberQuery
  refine String do
    def self.number?(str)
      !!str.match(/^[0-9]+$/)
    end
  end
end

class A
  using NumberQuery
  def a
    String.number?("123")
  end
end

A.new.a #=> NoMethodError: undefined method `number?' for String:Class

傻眼了吧? 呵呵,告訴你正確的定義類方法的用法:

 
module NumberQuery
  refine String.singleton_class do
    def number?(str)
      !!str.match(/^[0-9]+$/)
    end
  end
end

class A
  using NumberQuery
  def a
    String.number?("123")
  end
end

A.new.a #=> true

這次正常了。

Refinements還有個容易令Rubyist犯二的地方,就是你refine的方法,如果和類中的方法重名,還是會重寫掉那個方法的,當(dāng)然你可以也使用super。

 
module NumberQuery
  refine String do
    def to_s
      to_i
    end
  end
end

class A
  using NumberQuery
  def a
    "123".to_s
  end
end

String#to_s的方法被修改了,唉,難道這又是Monkey Patches的節(jié)奏? 不。 它只在A#a這個方法內(nèi)有用,不會污染到全局,但是你在碰到類似情況的時候,一定要注意。當(dāng)然,在A的子類,也會被傳下去,除非a方法被重寫。

 
class B < A; end
B.new.a #=> 123

#當(dāng)子類B中,重寫的a方法之后:
class B < A
  def a
    "123".to_s
  end
end
B.new.a #=> "123"

#除非你在B類也使用refine補丁
class B < A
  using NumberQuery
  def a
    "123".to_s
  end
end
B.new.a #=> 123

還有個值得說明的地方,就是,using的模塊,不會被掛到繼承樹(祖先樹/ancestors tree)上:

 
module NumberQuery
  refine String do
    def number?
      !!match(/^[0-9]+$/)
    end
  end
end

class A
  using NumberQuery
  def a
    "123".number?
  end
end

A.ancestors #=>  [A, Object, Kernel, BasicObject]

可以看到, NumberQuery并沒有被掛到祖先樹上。

到此,關(guān)于“Ruby2.1中Refinements特性有哪些”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識,請繼續(xù)關(guān)注億速云網(wǎng)站,小編會繼續(xù)努力為大家?guī)砀鄬嵱玫奈恼拢?/p>

向AI問一下細節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI