【问题标题】:Refactor with Strategy Pattern. In Ruby用策略模式重构。在红宝石中
【发布时间】:2019-12-05 22:37:00
【问题描述】:

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

目标

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

示例



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

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

【问题讨论】:

  • 你好像忘了问问题?
  • 在这个特定的例子中,我建议使用counter cache
  • hiphop = Genre.new; hiphop.songs.count怎么样

标签: ruby design-patterns activerecord refactoring strategy-pattern


【解决方案1】:

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

【讨论】:

  • 几乎一切皆有可能,但您要问的更多是关于元编程而不是策略模式。你想象的用途是什么?比如:genre.count(:songs)?或者只是genre.count,它期望通过标准输入给出集合。请注意,在 Rails 服务器中的最后一个用法很复杂,因为只有服务器主机可以提供标准输入。
  • 这很有帮助!你帮助我澄清了我的意图:例如,“你想数什么?”打印到屏幕上...用户可以输入“Korsakov 的歌曲”... 正则表达式将确定 Korsakov 是对象,并且程序会“知道”歌曲是集合。或“由 Korsakov 表演的音乐会”或“Korsakov 的学生”也许我们有一个识别关键字的函数:歌曲、音乐会、学生和艺术家(类似于 ||=),然后使用单个函数/魔术方法作为战略。无论如何,感谢您的反馈!
  • 类似 count_anything_in_the_object(object, the_anything) 的东西——我也很抱歉。我觉得作为一个新手的问​​题有一半是不能清楚地表达你的意思。不过,看到您的答案很棒,因为它帮助我更好地了解某些概念的起点和终点。
  • @AdamWeissman 我将在今天晚些时候扩展答案(并删除此回复)。我目前没有时间完全回答您的添加要求。不过我还有一个问题。由于您继承自 ActiveRecord::Base,因此我假设这涉及 Rails 应用程序,并且用户必须通过 Web 请求而不是标准输入来提供输入。
  • @3limini4t0r 非常感谢您的帮助,但请不要担心写出来...没有更大的应用程序。我以此为例来探索一个机制。感谢您的帮助,因为我现在意识到我必须深入研究元编程!
猜你喜欢
  • 2010-10-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-11-20
  • 2018-05-17
  • 2019-08-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多