我有一个 PORO (Plain Old Ruby Object),用于处理一些业务逻辑。它接收一个 ActiveRecord
对象并对其进行分类。为了简单起见,以以下内容为例:
class Classificator
STATES = {
1 => "Positive",
2 => "Neutral",
3 => "Negative"
}
def initializer(item)
@item = item
end
def name
STATES.fetch(state_id)
end
private
def state_id
return 1 if @item.value > 0
return 2 if @item.value == 0
return 3 if @item.value < 0
end
end
然而,我也希望进行基于这些“state_id”“虚拟属性”的对象分组查询。我目前通过在SQL查询中创建此属性并在“GROUP BY”语句中使用它来处理此问题。请参见以下示例:
class Classificator::Query
SQL_CONDITIONS = {
1 => "items.value > 0",
2 => "items.value = 0",
3 => "items.value < 0"
}
def initialize(relation = Item.all)
@relation = relation
end
def count
@relation.select(group_conditions).group('state_id').count
end
private
def group_conditions
'CASE ' + SQL_CONDITIONS.map do |k, v|
'WHEN ' + v.to_s + " THEN " + k.to_s
end.join(' ') + " END AS state_id"
end
end
这样,我可以将这个业务逻辑转化为SQL,并以非常高效的方式进行查询。
问题在于:我有重复的业务逻辑。它存在于“ruby”代码中,用于分类单个对象,也存在于“SQL”中,用于对数据库中的对象集进行分类。
这是一种不好的做法吗?有没有避免这种情况的方法?实际上,我通过以下方式成功地解决了这个问题:
item = Item.find(4)
items.select(group_conditions).where(id: item.id).select('state_id')
但这样做,我失去了对于未保存在数据库中的对象进行分类的能力。另一种解决方法是使用迭代器在Ruby中对每个对象进行分类,但这会损失数据库的性能。
如果要同时满足两种情况,似乎无法避免重复业务逻辑。但是我只是想确保一下这一点。 :)
谢谢!