使用策略模式进行重构。在 Ruby

Refactor with Strategy Pattern. In Ruby

注意!在下面的示例中,使用模式可能有点矫枉过正......但是,如果我将其扩展到计算流派,计算给定乐队中的成员,计算粉丝数量,计算播放场地的数量,计算销售的唱片,计算特定歌曲的下载次数等...似乎有很多东西要计算。

目标

创建一个根据输入选择正确计数函数的新函数。

例子:



class Genre < ActiveRecord::Base
  has_many :songs
  has_many :artists, through: :songs

  def song_count
    self.songs.length
  end

  def artist_count
    self.artists.length
  end

end

P.S。如果您也对这个问题感到好奇,您可能会发现这个其他问题(不幸的是在 C# 中回答)作为补充上下文很有帮助。 Strategy or Command pattern? ...

在 Ruby 中,您可以使用(可选)块(假设它仍未使用)很容易地实现策略模式。

class Genre < ActiveRecord::Base
  has_many :songs
  has_many :artists, through: :songs

  def song_count(&strategy)
    count_using_strategy(songs, &strategy)
  end

  def artist_count(&strategy)
    count_using_strategy(artists, &strategy)
  end

  private

  def count_using_strategy(collection, &strategy)
    strategy ||= ->(collection) { collection.size }
    strategy.call(collection)
  end
end

以上代码默认使用size策略。如果您想在特定场景中使用特定策略,您只需在调用时提供该策略即可。

genre = Genre.last
genre.song_count # get the song_count using the default #size strategy
# or provide a custom stratigy
genre.song_count { |songs| songs.count } # get the song_count using #count
genre.song_count { |songs| songs.length } # get the song_count using #length

如果您需要更频繁地重复使用某些策略,您可以将它们保存在常量或变量中:

LENGTH_STRATEGY = ->(collection) { collection.length }

genre.artist_count(&LENGTH_STRATEGY)

或者如果它们更复杂(目前有点矫枉过正),则为它们创建特定的 class:

class CollectionStrategy
  def self.to_proc # called when providing the class as a block argument
    ->(collection) { new(collection).call }
  end

  attr_reader :collection

  def initialize(collection)
    @collection = collection
  end
end

class LengthStrategy < CollectionStrategy
  def call
    collection.length
  end
end

genre.artist_count(&LengthStrategy)