Ruby中基于Enumerable类的方法链实现错误及优化求助
解决方法及优化建议
首先咱们拆解你遇到的问题,一步步来修复和优化代码:
1. 核心错误原因
你看到的undefined method '[]' for #<Coffee:...>错误,是因为在Criteria#each里把Coffee实例当成哈希来访问属性了——c[:strength]是哈希的写法,但c是Coffee对象,应该用它的实例方法c.strength来获取强度值。
2. 修正并增强Criteria#each方法
咱们不仅要修复错误,还要同时支持strength过滤和limit限制,并且让代码更具扩展性。这里是修正后的Criteria类:
class Criteria include Enumerable # 让Criteria直接支持Enumerable的所有方法(map/select等) def initialize(klass) @klass = klass end def criteria @criteria ||= { conditions: {}, limit: nil } end # 过滤指定强度的咖啡 def strength(strength) criteria[:conditions][:strength] = strength self # 返回self以支持方法链 end # 限制返回结果数量 def limit(limit) criteria[:limit] = limit self end def each(&block) # 第一步:根据条件过滤咖啡(支持未来扩展更多条件) filtered_coffees = @klass.collection.select do |coffee| criteria[:conditions].all? do |attr_name, target_value| coffee.send(attr_name) == target_value end end # 第二步:应用limit限制(仅当设置了limit时生效) filtered_coffees = filtered_coffees.take(criteria[:limit]) if criteria[:limit] # 第三步:遍历最终结果 filtered_coffees.each(&block) end end
3. 修复CoffeeShop的小问题
你的CoffeeShop.collection方法每次调用都会重新创建数组,这会导致数据丢失或重复初始化,咱们改成只初始化一次:
class CoffeeShop include Enumerable def self.collection # 用||=确保集合只初始化一次 @collection ||= [ Coffee.new("Laos", 10), Coffee.new("Angkor", 7), Coffee.new("Nescafe", 1) ] end def self.limit(*args) Criteria.new(self).limit(*args) end def self.strength(*args) Criteria.new(self).strength(*args) end # 实现Enumerable要求的each方法,让CoffeeShop本身也支持枚举 def each(&block) self.class.collection.each(&block) end end
4. 测试验证
现在运行你的测试代码:
CoffeeShop.strength(10).each { |c| puts c.inspect } # 输出:#<Coffee:0x00... @name="Laos", @strength=10>
还可以链式调用多个条件+Enumerable方法:
# 获取强度>=7的前1个咖啡的名字 CoffeeShop.collection.select { |c| c.strength >=7 }.limit(1).map(&:name) # 输出:["Laos"]
额外编码建议
- 扩展性优化:现在的
Criteria支持轻松添加更多条件方法(比如name),只需新增一个方法把条件加入criteria[:conditions]即可,不用修改过滤逻辑。 - 代码风格:Ruby里更推荐用符号作为哈希键(比如
:conditions而非"conditions"),保持代码风格一致性。 - Enumerable复用:让
Criteria包含Enumerable后,你可以直接使用map、select、sort等所有Enumerable方法,无需自己重复实现。
内容的提问来源于stack exchange,提问作者craig
相关产品推荐
相关产品推荐

