封装数组方法以实现更简洁、符合DRY原则的代码
复用
all?判断逻辑的优化方案 最近我在优化Rails代码时,遇到了重复逻辑的问题:两段分支里都要对父对象的children关联执行相同的all?判断,代码冗余得很,原始代码长这样:
if <condition> parent_1.children.all? {|c| <condition>} ? result_of_true : result_of_false else parent_2.children.all? {|c| <condition>} ? result_of_true : result_of_false end
我想把重复的all? {|c| <condition>}逻辑抽出来复用,一开始试了两个方案,但都没达到预期:
尝试的方案一:用Proc封装判断
我写了个返回Proc的方法,想着能直接调用:
def new_all? proc { |*args| args.all? {|c| c.attribute} } end # 调用代码 if <condition> parent_1.child.new_all? ? result_of_true : result_of_false else parent_2.child.new_all? ? result_of_true : result_of_false end
结果这个方法返回的是类似[false, false, true]的结果数组,不是all?应该返回的单个布尔值,完全不符合需求。
尝试的方案二:子类定义类方法
我还在子模型里加了个类方法:
def self.new_all? all? {|c| <condition>} end
可惜这个方法返回的也是结果数组,还是不对。
最终可行方案:关联扩展(Association Extensions)
后来收到了不少大佬的建议,其中@Stefan提到的关联扩展正是我要找的答案!最开始试的时候没成功,后来我深入研究了Rails scope的工作原理,搞懂了关联扩展的逻辑后终于搞定了。
具体做法是在父模型定义has_many :children关联的时候,直接给这个关联添加自定义方法:
class Parent < ApplicationRecord has_many :children do def all_meet_condition? all? { |c| <condition> } end end end
这样一来,所有父对象的children关联都能直接调用这个封装好的方法,简化后的代码就清爽多了:
if <condition> parent_1.children.all_meet_condition? ? result_of_true : result_of_false else parent_2.children.all_meet_condition? ? result_of_true : result_of_false end
不仅彻底消除了重复代码,而且返回的就是我们需要的单个布尔值,完美解决问题!
内容的提问来源于stack exchange,提问作者Nappy
相关产品推荐
相关产品推荐

