用于反向导航多对多关系的模式/结构?

时间:2011-09-16 15:05:04

标签: design-patterns activerecord data-structures many-to-many identity-map

用户故事:

我们的应用程序的用户创建了公路旅行。 roadtrip是一系列有趣的目的地。每个destination都有一些关于活动或景点的详细信息。通过这种方式,我们的用户定义了两次公路旅行,其中每次旅行都有一些独特的目的地和一些共同的目的地 - 例如。两次旅行都包括The Smithsonian。该应用程序维护内存中的所有更新,并仅在用户单击“保存”时提交到数据库。用户主动更新两次旅行,并可以随意切换。在我们的应用程序中,我们正在处理史密森尼目的地,但我们有时需要将目标层次结构从目的地导航到其包含的公路旅行。问题是目的地参加了两次公路旅行。

RoadTrip1
 |
 +-Destination1
 +-Destination2
 +-Destination3
 +-Smithsonian (A) //Navigate up to RoadTrip1

RoadTrip2
 |
 +-Destination4
 +-Smithsonian (B) //Navigate up to RoadTrip2
 +-Destination5

我们可以使用哪种好的设计模式或数据结构来允许向后导航,同时确保我们只有一个目标对象的副本?

要求:

  1. 您的模型涉及多对多关系。
  2. 在内存中代表所有模型一次(身份图)。
  3. 您的数据结构应易于导航。您不仅可以从父母导航到孩子,还可以从孩子导航回到最初获取孩子的父母。
  4. 我想避免在数据模型中引入其他架构。
  5. 到目前为止,我最好的想法是使用上下文对象包装每个目标对象(类似于链接列表如何包装节点)。上下文对象将维护一个指向最初从中获取它的父级的指针。我们总是通过它的包装器来处理每个目的地。我相信这将是代理或装饰模式(我倾向于代理)。 (这实际上与jQuery对象如何包含许多元素以及多个jQuery对象共享对相同元素的引用的想法基本相同吗?)

    我考虑维护一个“当前公路旅行”上下文变量,并将其用于从目的地导航到其包含的公路旅行。这不如实际的“获取上下文”可靠。事实上,这是一个完全不同的方式,我不确定我喜欢它。

    我记得我和ActiveRecord有同样的问题(虽然我用它已经有一段时间了)。在AR中,如果我开始使用RoadTrip1然后获取其目的地,我就无法很好地从目的地导航到公路旅行(通过某种获取上下文)。相反,我会考虑父母双方(公路旅行)而没有指示我如何到达那里。正确?

    让其他人遇到这个问题 - 也就是说,想要向后导航,许多家长会混淆后向导航?你有没有问过“我来自哪个家长?”你是怎么回答的?

2 个答案:

答案 0 :(得分:0)

您必须拥有3个课程:Roadtrip,Destination和Place。因此,您的目的地A和B是两个不同的对象,它们都指向同一个地方。

答案 1 :(得分:0)

我能够使用代理模式后的解决方案。我真正想要的是“获取上下文”的概念。我想知道我最初从哪个父母那里获取了一个模型(一个有多个父母的孩子)。

解决问题的关键是实现维护提取上下文是我们的查询对象而不是我们的模型的责任。

var activity = roadtrip.destinations().all().activities().first();

我们从roadtrip模型开始并调用destinations函数。此函数返回一个查询对象。此查询对象在设计上与Rails的Arel实现类似,因为它是懒惰的,在您调用allfirsteach等之前,实际上并未返回任何记录。查询对象具有context变量指向其父级 - roadtrip模型。

all调用返回一个集合对象,其context指向调用它的查询。集合中的每个项目都是一个包含每个基础queried item模型的代理(destination)。代理维护对其集合的引用。实现此代理的最简单方法是:

var proxied_destination = Object.create(destination);

通过这种方式,您可以将context分配给代理,而不会影响原始内容。

proxied_destination.context = collection;

这允许“主”模型保持不变,从而进行身份映射。如果我们的模型保持对其集合的直接引用,则这是不可能的,因为模型可以参与多个结果集(我们可以运行任意数量的查询),并且在我的场景中,我们只期望一个上下文(父级)。

我们调用activities,它为我们提供了另一个查询对象,其上下文是代理destination。我们调用first而不是获取集合,我们得到一个代理activity,其中包含指向活动查询对象的上下文。

因此,使用代理允许我们指向并因此“攀爬”对象层次结构,同时仍然保持身份映射模型,该模型仍然无视该层次结构,并且可以轻松地参与多个集合(结果集)。