Ruby/Rails学习:如何用规则类重构available_items方法?
这问题提得太到位了!把业务规则和核心类解耦,用策略模式来实现可扩展的规则集合,绝对是保持代码灵活性和可维护性的好路子。咱们一步步来实现你想要的效果:
1. 先搭好基础的策略容器类
首先创建一个专门用来管理规则的ItemAvailabilityPolicy类,它的职责就是接收规则数组,然后把这些规则依次应用到物品集合上:
class ItemAvailabilityPolicy def initialize(rules = []) @rules = rules end # 把物品集合和当前上下文(你的原类实例)传给规则 def apply(items, context) @rules.inject(items) do |filtered_items, rule| rule.apply(filtered_items, context) end end end
这里用inject的好处是,每个规则的输出会作为下一个规则的输入,规则之间可以链式组合,完全不冲突。
2. 把原有的时长规则封装成独立类
接下来把你原来的“时长≥365天排除保险”逻辑拆成单独的规则类,每个规则只干一件事,符合单一职责原则:
class ExcludeInsuranceForLongTermRule # 可以通过初始化参数自定义最小时长,默认365天 def initialize(min_duration = 365) @min_duration = min_duration end # 规则自己判断是否要生效,再执行过滤 def apply(items, context) return items unless context.duration >= @min_duration items.not_including_insurance end end
这里的context就是你原来的类实例,所以规则可以直接调用它的duration方法,完全不用和原类耦合。
3. 改造原类的
available_items方法 现在你的原类里就不用写复杂的判断逻辑了,只需要初始化策略并传入规则,然后调用apply就行:
# 替换成你实际的类名 class YourBookingClass def available_items # 初始化策略,把需要的规则传进去 policy = ItemAvailabilityPolicy.new([ ExcludeInsuranceForLongTermRule.new # 可以传参数比如ExcludeInsuranceForLongTermRule.new(365) ]) # 把物品集合和当前实例(上下文)传给策略 policy.apply(items, self) end # 原来的方法保留不动 def duration (start_on..end_on).count end def items space.available_items end end
这样一来,时长判断的逻辑完全从原类里剥离出去了,原类只需要专注于自己的核心职责。
4. 新增规则超简单!
以后要加新规则的时候,完全不用动原类的代码,只需要新建一个规则类,然后加到规则数组里就行。比如要加一个“周末隐藏特定物品”的规则:
class ExcludeWeekendExclusiveItemsRule def apply(items, context) return items unless Date.today.on_weekend? items.excluding_weekend_only_items end end
然后在原类里更新规则数组:
def available_items policy = ItemAvailabilityPolicy.new([ ExcludeInsuranceForLongTermRule.new, ExcludeWeekendExclusiveItemsRule.new # 直接加新规则 ]) policy.apply(items, self) end
是不是超级灵活?完全符合开闭原则——对扩展开放,对修改关闭。
额外优化小技巧
- 可以把规则类放到单独的目录里,比如
app/policies/item_rules/,这样代码结构更清晰,找起来也方便 - 可以给规则类加一个
applicable?方法,先判断规则是否需要生效,再执行过滤,让规则的逻辑更清晰:
class ExcludeInsuranceForLongTermRule def initialize(min_duration = 365) @min_duration = min_duration end def applicable?(context) context.duration >= @min_duration end def apply(items, context) applicable?(context) ? items.not_including_insurance : items end end
然后在策略类里调整apply方法:
def apply(items, context) @rules.inject(items) do |filtered_items, rule| rule.applicable?(context) ? rule.apply(filtered_items, context) : filtered_items end end
内容的提问来源于stack exchange,提问作者drifterOcean19
相关产品推荐
相关产品推荐

