在阅读了有关访问者模式的更多信息后,我认为它的 Rubyish 变体是解决此问题的最合适的方法。
访问者模式允许您将遍历层次结构的算法与要在层次结构中的每个节点上执行的代码分开。使用 map 或注入/折叠的更实用的方法是可能的……但由于我想重用运算符,将它们分成单独的类似乎更容易。
层次结构在每个模型中实现,它应该定义一个返回孩子的“孩子”方法。
下面是我的实现,基于各种参考,我可以将它包装成一个 gem。
module Visitable
def accept visitor
child_vals = []
if respond_to?(:children)
children.each do |child|
child_vals << child.accept(visitor)
end
end
val = visitor.visit(self)
child_vals.any? ? val + child_vals : val
end
end
class Survey
attr_accessor :name, :children
include Visitable
end
class Category
attr_accessor :name, :children
include Visitable
end
class Question
attr_accessor :name
include Visitable
end
s = Survey.new
s.name = 's1'
c = Category.new
c.name = 'c1'
c2 = Category.new
c2.name = 'c2'
q = Question.new
q.name = 'q1'
q2 = Question.new
q2.name = 'q2'
c.children = [q]
c2.children = [q2]
s.children = [c,c2]
class ReturnVisitor
def visit obj
obj.name
end
end
s.accept(ReturnVistor.new)
-> ['s1', ['c1', ['q1'], ['c2', ['q2']]]]
# "poorly implemented lisp"?