用户故事:
我们的应用程序的用户创建了公路旅行。 roadtrip
是一系列有趣的目的地。每个destination
都有一些关于活动或景点的详细信息。通过这种方式,我们的用户定义了两次公路旅行,其中每次旅行都有一些独特的目的地和一些共同的目的地 - 例如。两次旅行都包括The Smithsonian。该应用程序维护内存中的所有更新,并仅在用户单击“保存”时提交到数据库。用户主动更新两次旅行,并可以随意切换。在我们的应用程序中,我们正在处理史密森尼目的地,但我们有时需要将目标层次结构从目的地导航到其包含的公路旅行。问题是目的地参加了两次公路旅行。
RoadTrip1
|
+-Destination1
+-Destination2
+-Destination3
+-Smithsonian (A) //Navigate up to RoadTrip1
RoadTrip2
|
+-Destination4
+-Smithsonian (B) //Navigate up to RoadTrip2
+-Destination5
我们可以使用哪种好的设计模式或数据结构来允许向后导航,同时确保我们只有一个目标对象的副本?
要求:
到目前为止,我最好的想法是使用上下文对象包装每个目标对象(类似于链接列表如何包装节点)。上下文对象将维护一个指向最初从中获取它的父级的指针。我们总是通过它的包装器来处理每个目的地。我相信这将是代理或装饰模式(我倾向于代理)。 (这实际上与jQuery对象如何包含许多元素以及多个jQuery对象共享对相同元素的引用的想法基本相同吗?)
我考虑维护一个“当前公路旅行”上下文变量,并将其用于从目的地导航到其包含的公路旅行。这不如实际的“获取上下文”可靠。事实上,这是一个完全不同的方式,我不确定我喜欢它。
我记得我和ActiveRecord有同样的问题(虽然我用它已经有一段时间了)。在AR中,如果我开始使用RoadTrip1然后获取其目的地,我就无法很好地从目的地导航到公路旅行(通过某种获取上下文)。相反,我会考虑父母双方(公路旅行)而没有指示我如何到达那里。正确?
让其他人遇到这个问题 - 也就是说,想要向后导航,许多家长会混淆后向导航?你有没有问过“我来自哪个家长?”你是怎么回答的?
答案 0 :(得分:0)
您必须拥有3个课程:Roadtrip,Destination和Place。因此,您的目的地A和B是两个不同的对象,它们都指向同一个地方。
答案 1 :(得分:0)
我能够使用代理模式后的解决方案。我真正想要的是“获取上下文”的概念。我想知道我最初从哪个父母那里获取了一个模型(一个有多个父母的孩子)。
解决问题的关键是实现维护提取上下文是我们的查询对象而不是我们的模型的责任。
var activity = roadtrip.destinations().all().activities().first();
我们从roadtrip
模型开始并调用destinations
函数。此函数返回一个查询对象。此查询对象在设计上与Rails的Arel实现类似,因为它是懒惰的,在您调用all
,first
,each
等之前,实际上并未返回任何记录。查询对象具有context
变量指向其父级 - roadtrip
模型。
all
调用返回一个集合对象,其context
指向调用它的查询。集合中的每个项目都是一个包含每个基础queried item
模型的代理(destination
)。代理维护对其集合的引用。实现此代理的最简单方法是:
var proxied_destination = Object.create(destination);
通过这种方式,您可以将context
分配给代理,而不会影响原始内容。
proxied_destination.context = collection;
这允许“主”模型保持不变,从而进行身份映射。如果我们的模型保持对其集合的直接引用,则这是不可能的,因为模型可以参与多个结果集(我们可以运行任意数量的查询),并且在我的场景中,我们只期望一个上下文(父级)。
我们调用activities
,它为我们提供了另一个查询对象,其上下文是代理destination
。我们调用first
而不是获取集合,我们得到一个代理activity
,其中包含指向活动查询对象的上下文。
因此,使用代理允许我们指向并因此“攀爬”对象层次结构,同时仍然保持身份映射模型,该模型仍然无视该层次结构,并且可以轻松地参与多个集合(结果集)。