You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.06 09:12:50