ビジター パターンについて詳しく読んだ後、この問題を解決するには、その Ruby 風バリアントが最も適切なアプローチであると判断しました。
ビジター パターンを使用すると、階層内の各ノードで実行するコードから、階層をたどるアルゴリズムを分離できます。map または inject/fold を使用してこれに対するより機能的なアプローチが可能です...しかし、演算子を再利用したいので、それらを別々のクラスに分割する方が簡単に思えました。
階層は各モデルに実装され、子を返す「子」メソッドを定義する必要があります。
以下は、さまざまな参照に基づいた私の実装です。これを宝石にまとめることができます。
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"?