坚持复合模式实现 - ruby​​ on rails

时间:2017-09-20 23:04:02

标签: ruby-on-rails design-patterns composite

有没有人有一个很好的端到端示例,说明如何将来自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

2 个答案:

答案 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,则可以通过以下方式坚持:

  • 根节点将具有0 tree_code
  • root的孩子将拥有1,2,3,... tree_code(这些是第一代)
  • 具有tree_code为1的节点的第二个
  • 代将具有1.1、1.2,... 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,例如nametime(您可能希望保留它而不是在运行时分配它)和sub_tasks。而你只为叶子任务这样做。您通常不会将每个复合任务都保留到DB。

您可以使用STI(单表继承)来实现此模式。您可以创建一个基类Task < ActiveRecord::Base(假设为Rails4)。然后,您从此子类化LeafTaskCompositTask。然后,您可以将每个任务作为其中任何一个的实例持久化。如果您的任务需要非常不同的接口,那么您可能希望将每个接口子类化。如果每个属性甚至需要非常不同的属性,那么您可以考虑多元继承(即它们仍然是Task的子类,但它们使用自己的表)。