2014-10-08 35 views
1

感謝一些其他職位和閱讀,我理解單身/元類。我明白爲什麼我們想在課堂上使用它們。但我仍然不明白爲什麼我們想在實例對象上使用它們。我還沒有在實踐中看到它。在Ruby中,將方法添加到實例的單例類的用例是什麼?

我指的是這樣的:

class Vehicle 
    def odometer_reading 
     # some code 
    end 
end 

my_car = Vehicle.new 

def my_car.open_door 
    # some code 
end 

在第一個想到的,這似乎是一個壞主意,因爲它會導致難以理解代碼和調試。

我們爲什麼要這麼做?什麼時候這是一個好主意的例子?

回答

2

一個例子是將它用於測試目的:創建模擬和雙重對象,存根方法。調試是在附近的某處:重新定義特定對象的日誌記錄方法,您懷疑該記錄方法是錯誤行爲,以便在調試會話期間將日誌信息直接打印到控制檯(或打印更多信息)。

另一個例子是處理特殊情況 - 而不是繼承你可以做到這一點。從經典示例開始,如果您使用兩種類型的Employee s,即Engineer s和SalesPerson s,對於該類型,補償計算的規則不同,可以將公共邏輯放入Employee類中,然後繼承其他兩個類它並在那裏實施他們自己的calculate_salary方法。現在,如果有一個局外人 - 你已經同意採用不同的薪酬方案的明星推銷員,擁有非常特殊的計劃的首席執行官等 - 而不是爲這個特殊員工創建一個完整的子類,那麼你可以定義這個方法來表示該員工的特定對象。

第三個例子是處理對象生命週期和性能考慮因素。在一些處理方法中,不是具有各種狀態的長的case。例如。對於在後臺透明地緩存整個文件的文件讀取類(我知道一個過於簡單的實際生活方式,但只是作爲一個模型),所有讀取請求,而文件沒有完全讀取時,應該檢查請求的數據已經在緩存中,或者應該從磁盤讀取。一旦文件被完全讀取,它們總是從緩存中移出。如果存在更多狀態,則不需要有ifcase)來處理此問題,只需在文件完全讀入緩存後,就可以在對象級重新定義read方法。對於這個簡單的例子,它不會帶來任何顯着的性能優勢(如果有任何好處的話),但對於更復雜的情況可能是值得的。

+0

感謝您的具體例子。如果我們這樣做,編程社區就被認爲是良好的做法,更不用說Ruby社區了?正如你所看到的那樣,我很難把這個想法看得很清楚。 – guptron 2014-10-08 21:20:46

+0

是的,對我來說似乎是一個很好的做法 - 非常Ruby-way-ish。 一些(很快)谷歌搜索後的鏈接: http://blog.flatironschool.com/post/67061115355/demystifying-singleton-classes-and-methods-他們認爲這是很好的(&使用下引擎蓋反正類方法)。 這篇文章(http://bettong.net/2011/11/07/notes-on-eloquent-ruby-number-3/)提到了「雄辯的紅寶石」書中提出的這種方法。現在,我手邊沒有這本書,但這本書幾乎是經典的(對我來說肯定是這樣),作者說這很好,並且是適當的「Ruby方式」的例子,那麼很可能就是這樣。 – moonfly 2014-10-08 21:43:01

1

你不會使用def來添加它們,這是一種非常僵硬的做法,而是使用類似define_methodextend的東西。

雖然這不是你常做的事情,但這確實意味着你可以做一些相當不尋常的事情。 Rails中的ActiveRecord以Array的形式生成結果,並添加其他方法來執行其他操作。

一個對象 - 關係映射器會是一個你可能想要這樣做的情況。有時,根據您如何獲取記錄,可用的方法差異很大。能夠動態添加這些對象意味着每個獲取的對象可以完全自定義,即使它們具有相同的類和通用方法。

+0

我明白了。當你說「做一些相當不尋常的事情」時,那就是我來自的地方。我們爲什麼想要做不尋常的事情?我想在非常特殊的情況下,這樣做相對安全,這很好。 – guptron 2014-10-08 21:24:58

+0

這裏是一個例子[在另一個問題](http://stackoverflow.com/questions/26266552/refactoring-ruby-scraping-code)使用元編程,響應文檔的結構。 – tadman 2014-10-08 21:26:58

0

另一個例子:你有一個散列數組,你希望每個散列都有一個方法調用getter和setter。喜歡的東西:

user = HashOnSteroids.new(name: 'John') 
user[:name] # => 'John' 
user[:name] = 'Joe' 
user.name # => 'Joe' 
user.name = 'John' 
user.set(name: 'Jim', age: 5) 

這意味着你不能寫在類的標準方法的定義,因爲每個哈希將有一個不同的密鑰集(方法名稱)。這意味着你不得不求助於定義單例方法,因此每個對象都有自己的一套方法(而不是一組共享方法)。

警告:對此用例使用單例方法效率非常低。一個鬼鬼祟祟的method_missing速度更快,並且使用少得多的內存,因爲它不必分配十億個proc對象。

+0

[OpenStruct](http://www.ruby-doc.org/stdlib-2.0/libdoc/ostruct/rdoc/OpenStruct.html)與此類似。 – tadman 2014-10-08 21:27:44

+0

OpenStruct看起來很像Javascript。 – guptron 2014-10-08 21:36:18

+0

我知道,但它吃了大約5倍的內存(MRI),似乎不支持批量設置。滾動我們自己的解決方案實際上很簡單,也很有趣。 – Ollie 2014-10-08 21:40:33