2013-11-27 38 views
-1

通常情況下,我有以下情況:我實現了一個只獲取一些輸入並返回輸出的算法。標準OO方法很可能是創建與輸入初始化的對象,然後調用一個方法在其上返回的結果(可能與「跑步」算法的中間步驟):使用類方法:在這個用例中它們是「邪惡的」嗎?

algo = MyAlgorithm.new(some_input) 
algo.run 
algo.result 

有是我不喜歡這樣幾件事情:

  • 每次我想使用這個類,我要記下所有的步驟,這是乏味的,很明顯。

  • 我的對象有(至少)2種不同的狀態:在運行算法之前和之後。儘管我只對第二個狀態感興趣,但我必須在我的規格中覆蓋第一個狀態的行爲。如果我在第一個狀態中撥打#result怎麼辦? #run冪等?

  • 我實際上違反了告訴不要問這裏:我要求班級給我一個對象,我用它來獲得我的問題的解決方案。爲什麼我不直接向班級詢問解決方案?

  • 最天真的實現是隻做一個方法調用。當然,純粹的命令式編程在這裏並不理想,因爲使用對象是有好處的:我們可以分離在算法計算過程中使用的數據。但是,這不僅僅是一個實現細節,如果是的話,我們不應該隱藏這些信息嗎?

我想知道爲什麼不只是做這樣的事情:與類對象的一個​​方法調用

class MyAlgorithm 

    def self.solve(input) 
    new(input).instance_eval { run; result }   
    end 

    def initialize(input) 
    @input = input 
    end 

    private 

    def run 
    # do some computation (modifying the object's state) 
    end 

    def result 
    # return the result 
    end 

end 

這樣,我得到的唯一的事情我真的想(結果),無失去了面向對象的好處。我沒有任何公共實例方法,所以我不必擔心我的對象的不同狀態。而且,如果我願意,我甚至可以使.new成爲私人方法。 (請注意,如果我對像運行時,迭代次數等報告感興趣,直接訪問對象是有道理的,但在這裏我只是對計算結果感興趣)。

現在我在這裏人們說「班級方法是邪惡的」(例如herehere)。但我認爲對於我的用例來說,類方法是正確的,不是嗎?另一方面,我想知道爲什麼我沒有看到其他人使用這種風格。也許我錯過了一些東西。這種設計有什麼缺點嗎?

+0

downvoters幹界面有點更加詳細:請建設性並添加評論。 –

回答

1

我可能會移動你的run方法私人部分,然後重寫你的結果的方法來折騰這樣的:

def result 
    @result ||= run 
end 

這會在你的對象的生命時間運行run方法一次,以及將封裝內部的邏輯類。或者,你可以在你的初始化運行run

def initialize(input) 
    @result = run(input) 
end 

def result 
    @result 
end 

我也看不出什麼毛病,使用一個類的方法 - 這不是C#,它不會改變類的狀態,它使更多的感覺比創建單一類只有目的是避免類方法。

+0

當然,你的例子展示瞭如何讓實例方法是冪等的,所以我只有一個狀態。但是我列表中的第一個和第三個重點仍然適用於此。 –

+0

我絕對同意你的觀點,我會在這裏使用類方法。唯一的情況可能是一個壞主意,如果你不得不在未來某個時候擴展你的算法,所以它可能會根據外部因素返回不同的值(所以你可以例如用不同的選項多次調用結果,而不需要每次重新運行算法)。使用類方法顯然可以解決問題1和3,但是如果只有兩個步驟,第一項並不是什麼大問題。 – BroiSatse

3

不,你在這裏使用類方法不是「邪惡」。但請繼續閱讀...

在編程中,數據就像名詞,算法就像動詞一樣。在一些像Java一樣的OO語言,哲學是一切都是一個對象:既nouns and verbs。有了這個理念,爲每個算法創建一個新類別的確是「標準OO方式」。

在Ruby中,我們以不同的方式做事。每個的東西是一個客體,即只有名詞。動詞是方法。如果動詞只對某個名詞有意義,它是該類的一個實例方法。如果它更一般化,它將被添加到一個模塊中以混合到幾個類中。如果它沒有意義屬於任何對象,則它被定義爲頂級方法或封裝在將被調用(實質上)沒有接收器的模塊中。

因此,儘管從技術上講,您對類設計模式的使用適用於這種設計模式,但模式本身會讓您從大多數Ruby專家看起來很怪異。用你在問題中給出的例子,我沒有看到爲你的算法上課有什麼好處。這對你來說是更多的工作,它不是慣用的,它提供了一些機會來破壞算法的使用。

你是正確的,有一個類可以使設計的某些方面優雅的你應該以後要添加對算法的狀態或性能更多的報告,但你影響您的設計的簡單,現在,以讓你的東西可能加上更簡單。如果它不在班級中,您仍然可以添加到該算法中,儘管它可能不夠優雅。

底線是,它看起來像所有你想要的是一種方法。但是你將這個方法隱藏起來就像方法一樣。但是,你通過將它們放在類似模塊的類中來隱藏實例。爲什麼?只需使用方法或模塊。

+0

我的課不像Module一樣。好的 - 從外面看,它的確如此,但重點是它創建了一個對象,其下面有附加狀態(而不是類)的好處。如果我在計算過程中不需要存儲狀態,當然一個模塊將是我的選擇。 –

+0

我明白。但是你的問題是專門針對「一種只獲得一些輸入並返回輸出的算法」。你不斷暗示需要保持狀態,但沒有一個可靠的例子,很難告訴你。還要記住,Ruby中的「一切都是對象」意味着即使模塊是對象,也就是說,您可以將狀態存儲在模塊中。只有在「實例」的概念適當的情況下才需要類。 – Max

+0

是的,但如果我在模塊中存儲狀態,它是全局的,在併發性方面存在問題,不是嗎? –

0

我會覺得MyAlghorithm.result作爲快捷方式

instance = new(input) 
instance.run 
instance.result 

考慮這樣的事情:

class MyAlgorithm 
    def self.result(input) 
    new(input) do |o| 
     o.run 
     o.result 
    end 
    end 

    def initialize(input) 
    @input = input 
    yield self if block_given? 
    end 

    # Property, doesn't modify the state, returns the property 
    attr_reader :result # (same as `def result; @result; end`, by the way) 

    # Action, modifies the state; it should either `return nil` on success 
    # or `raise Something` on error 
    def run 
    # do some computation (modifying the object's state) 
    @result = ... 
    nil 
    end 
end 

如果您有由對象修改多個狀態:

class MyAlgorithm 
    # access via 
    # results = MyAlgorithm.results(input); results.result1; results.result2 
    def self.results(input) 
    new(input) do |o| 
     o.run 
     o 
    end 
    end 

    def initialize(input) 
    @input = input 
    yield self if block_given? 
    end 

    attr_reader :result1, :result2 

    def run 
    @result1, @result2 = ..., ... 
    nil 
    end 
end 

每次我想要使用這門課時,我必須寫下所有的步驟,這顯然很乏味。

使用解決MyAlghorithm.result input

我的對象具有(至少)2種不同的狀態:前和運行算法之後。儘管我只對第二個狀態感興趣,但我必須在我的規格中覆蓋第一個狀態的行爲。如果我在第一個狀態中調用#result會怎麼樣? #run是冪等的嗎?

#run應該return nil,因爲它修改狀態;那麼你可以撥打result並獲得價值。規格照常完成。

我實際上違反了告訴別在這裏問:我要求班級給我一個對象,我用它來獲得我的問題的解決方案。爲什麼我不直接向班級詢問解決方案?

在哪個意義上?你的意思是在班級範圍內實施一切嗎?這是不方便的,因爲你失去了面向對象的優點

最天真的實現將只是一個方法調用。當然,純粹的命令式編程在這裏並不理想,因爲使用對象是有好處的:我們可以分離在算法計算過程中使用的數據。但是,這不僅僅是一個實現細節,如果是的話,我們不應該隱藏這些信息嗎?

這就是我認爲MyAlghorithm.result作爲常用東西的捷徑的原因;你可以在OO實現(不使用的run返回值,並使用result代替,...),並使用類方法

相關問題