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
PS 如果您也对这个问题感到好奇,您可能会发现这个其他问题(不幸的是用 C# 回答)作为补充上下文很有帮助。战略还是命令模式?...
解决方案
在 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)
推荐阅读
- ubuntu - 网络管理器 gsm 连接
- python - 不明白这个转换程序的elif语法错误
- python - PyQt5如何将QLabel更新为动画
- material-ui - 如何覆盖深度嵌套组件的样式?(Material-UI Jss 样式)
- mysql - 多主机 docker-compose 与数据库的静态内部 IP
- python - 需要与 tkinter 在同一窗口中的条目表吗?如何使用 tktable 获取条目?
- java - 错误:尝试构建项目时 [Dagger/MissingBinding]
- git - 在 DockerFile 中使用主机 SSH 密钥访问私有 Git 存储库
- python - Jupyter Notebook 格式错误的 http 消息来自 1 内容长度太长
- python - Pytest:在测试中使用夹具的参数化输出作为类实例名称