我在Ruby中有一个包含一些内容的类,我会调用FooBox
:
class FooBox
...
end
我有两个可能的FooBox
支持数据存储,名为BoxA
和BoxB
,具有不同的特征,但界面相同:
class BoxA
include Enumerable
def put_stuff(thing)
...
end
end
class BoxB
include Enumerable
def put_stuff(thing)
...
end
end
如何实例化FooBox
,并根据参数决定是使用BoxA
还是BoxB
实施来支持它?我不想将实现传递给构造函数;我只想传递一些东西来确定使用哪种。
class FooBox
def initialize(implementation_choice)
# ???
end
end
答案 0 :(得分: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
这使得“知道”策略是否能够在策略本身中使用(而不是使上下文类单片化),然后只需选择列表中的第一个可以工作的人。< / p>
我已经多次使用这种模式(以及针对略有不同的问题的类似变化)并且发现它非常干净。
答案 1 :(得分:0)
简单的解决方案是为策略的类型和策略类创建一个映射,就像@Andrew Marshall的解决方案一样
但为了更好,我会考虑两件事:
FooxBox
)现在需要了解每个盒子的实现,并将其名称硬编码给自己;这不灵活
方法,考虑有一天你想添加另一个策略,转到代码并添加它?使用ruby,我们可以轻松地进行“自我注册”。interface
,使用ruby我们通常使用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