有没有人有一个很好的端到端示例,说明如何将来自GoF的教科书复合模式保存到数据库,如下所示?
组件基类:
class Task
attr_reader :name
def initialize(name)
@name = name
end
def get_time_required
0.0
end
end
复合类:
class CompositeTask < Task
def initialize(name)
super(name)
@sub_tasks = []
end
def add_sub_task(task)
@sub_tasks << task
end
def remove_sub_task(task)
@sub_tasks.delete(task)
end
def get_time_required
time = 0.0
@sub_tasks.each { |task| time += task.get_time_required }
time
end
end
答案 0 :(得分:1)
迟到将近一年,但是我觉得扩大答案仍然很有用。您将为每个任务都需要一个ID,假设该ID是任务的名称。然后,在Task中使用新的父属性,您将能够保留Composite的树结构:
class Task
attr_reader :name
attr_reader :parent
def initialize(name, parent=nil)
@name = name
@parent= parent
end
def get_time_required
0.0
end
end
通过这种方式,您将能够从父级导航到其子级select * from tasks where parent_id = ?
或从某个子级导航到其父级select * from tasks where id = ?
。
另一个技巧是使用索引,如本章中所述,以检索某个节点下的所有层次结构。如果您提交了tree_code
,则可以通过以下方式坚持:
tree_code
,tree_code
(这些是第一代)tree_code
为1的节点的第二个tree_code
。对于具有tree_code
2的节点将具有2.1、2.2,... tree_code
然后,您将可以通过查询select * from tasks where tree_code like '1.%'
来检索任务1的所有子节点。
这始终是插入/更新与选择之间的性能折衷,因为在保留任何组件之前必须计算tree_code
。
如果您使用的是Rails应用程序,则可以始终使用ancestry
答案 1 :(得分:0)
您只需要将属性保存到db,例如name
和time
(您可能希望保留它而不是在运行时分配它)和sub_tasks
。而你只为叶子任务这样做。您通常不会将每个复合任务都保留到DB。
您可以使用STI(单表继承)来实现此模式。您可以创建一个基类Task < ActiveRecord::Base
(假设为Rails4)。然后,您从此子类化LeafTask
和CompositTask
。然后,您可以将每个任务作为其中任何一个的实例持久化。如果您的任务需要非常不同的接口,那么您可能希望将每个接口子类化。如果每个属性甚至需要非常不同的属性,那么您可以考虑多元继承(即它们仍然是Task
的子类,但它们使用自己的表)。