2013-03-11 39 views
0

我在Ruby中一類擁有一些東西,我會叫FooBox如何在實例化時切換實現?

class FooBox 
    ... 
end 

我有一個名爲BoxABoxB具有不同的特性,但在同一界面FooBox兩種可能的後備數據存儲:

class BoxA 
    include Enumerable 
    def put_stuff(thing) 
    ... 
    end 
end 


class BoxB 
    include Enumerable 
    def put_stuff(thing) 
    ... 
    end 
end 

如何實例化FooBox,並根據參數決定是否使用BoxABoxB實現來支持它?我不想將實現傳遞給構造函數;我只想傳遞一些信息來決定使用哪種類型。

class FooBox 
    def initialize(implementation_choice) 
    # ??? 
    end 
end 
+0

您可以更具體地瞭解'FooBox'如何使用這些實現?如果你傳遞了一個實際的實現實例或一個abitrary標誌,爲什麼它很重要,你試圖避免什麼? – PinnyM 2013-03-11 03:26:44

+0

所以你想在Ruby中實現工廠設計模式? – donnior 2013-03-11 03:34:41

+0

FooBox的消費者不知道支持的實現,它只是想如何處理數據(從抽象意義上講)。 FooBox查看參數,然後顯示「我看到你在這裏所要求的東西,讓我爲此進行實施。」 – 2013-03-11 03:36:24

回答

1

我平時做這樣的事情:

class BoxA 
    def self.match? options 
    # figure out if BoxA can be used given options 
    end 
end 

# Implement BoxB (and other strategies) similarly to BoxA 

class FooBox 
    STRATEGIES = [BoxA, BoxB] 

    def initialize options 
    @options = options 
    end 

    def strategy 
    @strategy ||= STRATEGIES.detect { |strategy| strategy.match? @options } 
    end 
end 

這使「知道」如果戰略是能夠對戰略本身(而不是使上下文類單片)中使用的責任,然後在列表中選擇第一個可以工作的第一個。

我已經多次使用這種模式(以及類似的變化,對於稍微不同的問題),並發現它很乾淨。

+0

不錯。這是好東西。謝謝。 – 2013-03-11 03:38:47

0

簡單的解決方案是創建一個策略的類型和策略類的映射,就像@Andrew馬歇爾的解決方案

但要更好,我會考慮兩件事情:

  • 的策略持有人(這裏是FooxBox)現在需要知道每個盒子的實現,並將它們的名字硬編碼到它自己;這不是一種靈活的方法,考慮有一天你想添加另一種策略,去代碼並添加它?隨着紅寶石,我們可以輕鬆地進行「自我註冊」。
  • 你不希望策略持有者狂甩地返回實現,我的意思是'BoxA'和'BoxB'或者某天'BoxXYZ'應該屬於同一個策略 概念,在Java中,它可能意味着它們都應該實現一個interface,紅寶石我們一般用include SomeMoudle

在我的應用程序採用如下方案(只是演示)做

module Strategies 
    def self.strategies 
    @@strategies ||= {} 
    end 

    def self.strategy_for(strategy_name) 
    @@strategies[strategy_name] 
    end 
end 

module Strategy 
    def self.included(base) 
    base.class_eval do 
     def self.strategy_as(strategy_name) 
     Strategies.strategies[strategy_name] = self 
     end 
    end 
    end 
end 


class BoxA 
    include Strategy 

    strategy_as :box_a 

    def do_stuff 
    puts "do stuff in BoxA" 
    end 
end 

class BoxB 
    include Strategy 

    strategy_as :box_b 

    def do_stuff 
    p "do stuff in BoxB" 
    end 
end 

## test 
Strategies.strategy_for(:box_a).new.do_stuff 
Strategies.strategy_for(:box_b).new.do_stuff 

如果要檢測與匹配塊的策略,你可以改變strategy_as接受一個塊。然後使用Strategies.strategy_for{...}.new.do_stuff

+0

沒錯,儘管我更喜歡像'Strategies.add_strategy BoxA'這樣的東西,而不是進入Strategies的內部數據結構。 Ruby沒有接口,'include'不能代替它們。您也可以通常在「Strategy」中定義'strategy_as'(而不是在鉤子中)和'extend Strategy'。 – 2013-03-11 04:57:51

+0

@AndrewMarshall謝謝,這是我的錯誤,我只想說'包括SomeMoudle',雖然它不是接口的替代品,但它比它強大。我個人喜歡包括模塊,^ _ ^ – donnior 2013-03-11 05:06:36