2013-05-19 12 views
0

我是一名經驗豐富的Obj-C/Java程序員,正在進入Ruby。很顯然,它的動態性非常好(重新開放的類很棒!),但是當我開始編寫Ruby代碼時,有一件事讓我感到擔憂。在Ruby中明確設置iVar類(ala Obj-C)

我很想知道你的Ruby-ERS做(如果有的話)明確設置實例變量的類型,在你自己的類。從我所看到的,你可以設置一個iVar任何對象,紅寶石不會抱怨。但是,如果您希望特定的iVar屬於某種類型,那麼它可能會導致問題。例如:

class MyString 
    def initialize(myString) 
    @myString = myString 
    end 

    def uppercase_my_string 
    @myString.upcase 
    end 
end 

st1 = MyString.new("a string!") 
st1.uppercase_my_string 

st2 = MyString.new(["a string"]) 
st2.uppercase_my_string 

該代碼將拋出NoMethodError,由於當然陣列沒有方法upcase。遺憾的是它並沒有告訴我們,我們真的出了問題(上面的線,創造str2時),所以在調試時我們沒有幫助很大(如果str2發生在一些不顯眼的地方創建幾個模塊遠) 一個很自然的一步可能是添加一些檢查initialize如下:

class MyString 
    def initialize(myString) 
    raise TypeError, "myString iVar is not a string!" unless myString.class == String 
    @myString = myString 
    end 
end 
...same code as before 

太好了,現在如果我們不小心創建一個新的MyString我們被告知我們如何傻了(更重要的是,當我們做到這一點,我們被告知,而不是當我們我的下一個問題是當我們決定在iVar上使用attr_accessors時。

class MyString 
    attr_accessor :my_string 
    def initialize(my_string) 
    raise TypeError, "myString iVar is not a string!" unless my_string.class == String 
    @my_string = my_string 
    end 

    def uppercase_my_string 
    @my_string.upcase 
    end 
end 

st1 = MyString.new("a string!") 
st1.uppercase_my_string 

st2 = MyString.new("good, it's a string") 
st2.my_string = ["an array!"] 
st2.uppercase_my_string 

使用規定的制定者,我們可真卑鄙,避開錯誤的initialize檢查。再次有這個問題拋出異常uppercase_my_string而不是當我們不小心設置@my_string到一個數組。

最後,我們可以手動創建的存取,並添加錯誤檢查,但是這是一個巨大的痛苦...有一個更快,更簡單的方式來做到這一點。 或者我只是想關閉頭腦,不夠動態?

謝謝!


題外話:我知道,在對象 - 你仍然在運行同樣的問題,但通常你會發現編譯器錯誤說你要指定array類型的對象,以string類型的變量(或類似的東西),所以至少我們警告它發生在哪裏

回答

4

實際上,這些各種各樣的類型的問題實際上是相當罕見的。如果你想成爲偏執(因爲他們真的是你,讓你),你可以通過to_s發送您的意見,以確保您始終有一個字符串:

def initialize(my_string) 
    @my_string = my_string.to_s 
end 

那麼你可以說MyString.new(6),一切都將按預期工作。當然,你可以說MyString.new([6, 11])並得到廢話。

如果你真的想my_string是你不會明確地檢查其class的String。這將導致問題,如果有人子類的字符串,所以你會想至少使用is_a?

def initialize(myString) 
    raise TypeError, ... unless myString.is_a? String 
    @myString = myString 
end 

還有一個to_str方法,你可以檢查:

def initialize(myString) 
    raise TypeError, ... unless myString.respond_to? :to_str 
    @myString = myString.to_str 
end 

是落實將方法(在一些圈)表明你的東西足夠像字符串一樣。我認爲調用to_s會是一個更好的主意,但這會讓人的行爲更像Ruby中的人們所期望的那樣。

至於你的問題的mutator關注:

st2.my_string = ["an array!"] 

你沒有讓任何人寫任何他們想要進入你的屬性:類不結構。你只能自動定義的訪問,寫自己的突變在你to_s呼叫會自動打滑:

class MyString 
    attr_reader :my_string 
    def initialize(my_string) 
    self.my_string = my_string 
    end 

    def my_string=(s) 
    @my_string = s.to_s 
    end 

    def uppercase_my_string 
    @my_string.upcase 
    end 
end 

基本上,你不用擔心,在Ruby的多,你擔心什麼方法什麼迴應類型。而且,如果你想要一些特別的字符串,你可以通過調用通用的to_s方法(這是什麼字符串插值,"#{x}")來使它成爲字符串。

+0

「你不用擔心Ruby中的類型太多,你擔心什麼方法響應」 - 什麼方法響應*是它的類型。 –

+0

謝謝,我儘量多的,但只是想知道是否有辦法 - 似乎沒有,但最有可能的是,因爲它不是紅寶石般的 - 這樣做。 'is_?'方法的好處,我將在未來使用它來檢查類/類的成員。 – Patrick

+0

Ruby中有很多DWIM(和大多數情況一樣,好的和不好的:)一般來說,儘管使用更窄的'respond_to?'來支持'is_a?';當然,在現實世界中,你必須切合實際並做任何事情。 –

2

我想如果你的程序將錯誤類型的對象傳遞給一個類,那麼這不是一個真正的問題,應該通過被實例化或變異的對象來解決。即使有額外的類型檢查和拋出的錯誤拋出,你的程序仍然把錯誤的對象傳遞給你的類,並沒有真正讓你的程序更不正確。

在一個使用MyString類的程序中,應該有一個contract,例如一個協議,即給予構造函數的對象實現upcase方法。

測試和文檔也被用來禁止程序中的這些缺陷。但我認爲這可能是一個單獨的討論。

+0

+1的鏈接接觸定義維基百科 你說得對 - 單元測試和文檔似乎喜歡的方式去(例如,如果讓其他人會使用一個lib) – Patrick

+0

我只是想補充一點,編寫共享代碼時(就像寶石一樣),某些類型檢查可能對其他人遇到問題有幫助。但總是使用respond_to?因爲提供什麼類別並不重要,它只需要滿足特定的要求。如果您檢查了班級,則所提供的班級必須從該班級下降,這可能不合適。 –