0

我在 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

如何实例化 a FooBox,并根据参数决定是否使用 aBoxABoxB实现来支持它?我不想将实现传递给构造函数;我只想通过一些东西来确定使用哪种。

class FooBox
  def initialize(implementation_choice)
    # ???
  end
end
4

2 回答 2

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

这保留了“了解”策略是否能够在策略本身中使用的责任(而不是使上下文类成为整体),然后只选择列表中表明它可以工作的第一个。

我已经多次使用这种模式(以及针对稍微不同的问题的类似变体)并且发现它非常干净。

于 2013-03-11T03:30:57.450 回答
0

简单的解决方案是为策略类型和策略类创建一个映射,就像@Andrew Marshall 的解决方案一样

但为了更好,我会考虑两件事:

  • FooxBox策略的持有者(这里是 这不是一种灵活的方法,考虑到有一天你想添加另一种策略,去代码并添加它?使用 ruby​​,我们可以轻松地通过“自我注册”来做到这一点。
  • 你不希望策略持有者会疯狂地返回实现,我的意思是 'BoxA' 和 'BoxB' 或者有一天的 'BoxXYZ' 应该属于同一个策略概念,在 Java 中,这可能意味着它们都应该实现一个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

于 2013-03-11T04:34:38.433 回答