用于反向导航多对多关系的模式/结构?
用户故事:
我们应用程序的用户创建公路旅行。 公路旅行
是一系列连续的有趣目的地。每个目的地
都有一些有关在那里进行的活动或景点的详细信息。通过这种方式,我们的用户定义了两次公路旅行,其中每次旅行都有一些独特的目的地和一些两者共同的目的地——例如,两次旅行都包括史密森学会。该应用程序在内存中维护所有更新,并且仅在用户单击“保存”时才提交到数据库。用户主动更新这两个行程,并且可以随意切换。在我们的应用程序中,我们有时会处理史密森尼目的地,但有时我们需要将对象层次结构从目的地导航到其包含的公路旅行。问题是目的地参加了两次公路旅行。
RoadTrip1
|
+-Destination1
+-Destination2
+-Destination3
+-Smithsonian (A) //Navigate up to RoadTrip1
RoadTrip2
|
+-Destination4
+-Smithsonian (B) //Navigate up to RoadTrip2
+-Destination5
我们可以使用什么好的设计模式或数据结构来允许向后导航,同时确保我们只有目标对象的一个副本?
要求:
- 您的模型涉及多对多关系。
- 所有模型仅在内存中表示一次(身份映射)。
- 您的数据结构应该易于导航。您不仅可以从父级导航到子级,还可以从子级导航回到最初获取子级的父级。
- 我想避免向数据模型引入额外的模式。
到目前为止,我最好的想法是用上下文对象包装每个目标对象(类似于链表包装节点的方式)。上下文对象将维护一个指向最初从中获取它的父对象的指针。我们总是通过其包装来处理每个目的地。我相信这将是代理模式或装饰器模式(我倾向于代理模式)。 (这本质上不是与 jQuery 对象包含许多元素并且多个 jQuery 对象共享对相同元素的引用的想法相同吗?)
我考虑维护一个“当前公路旅行”上下文变量并使用它从目的地向上导航到其包含的公路旅行。这并不像实际的“获取上下文”那么可靠。事实上,这是一种完全不同的策略,我不确定我是否喜欢它。
我记得 ActiveRecord 也有同样的问题(尽管我已经有一段时间没有使用它了)。在 AR 中,如果我从 RoadTrip1 开始,然后获取其目的地,我就无法很好地从目的地导航回公路旅行(通过某种获取上下文)。相反,我需要考虑父母双方(公路旅行),并且没有任何迹象表明我是如何到达那里的。正确的?
其他人以前是否遇到过这个问题——也就是说,想要向后导航,而向后导航却被许多家长所困惑?你有没有问过“我是从哪位父母那里来到这里的?”你是怎么回答的?
A user story:
The user of our app creates road trips. A roadtrip
is a sequential series of interesting destinations. Each destination
has some details about an activity or sight to see while there. In this way, our user defines two road trips where each trip had some unique destinations and some destinations common to both--e.g. both trips include The Smithsonian. The app maintains all updates in memory and only commits to the database when the user clicks save. The user actively updates both trips and can switch between them at will. At points in our app we’re dealing with the Smithsonian destination but we sometimes need to navigate up our object hierarchy from the destination to its containing road trip. The problem is that the destination takes part in two road trips.
RoadTrip1
|
+-Destination1
+-Destination2
+-Destination3
+-Smithsonian (A) //Navigate up to RoadTrip1
RoadTrip2
|
+-Destination4
+-Smithsonian (B) //Navigate up to RoadTrip2
+-Destination5
What's a good design pattern or data structure we could use to allow for backward navigation while assuring we have just one copy of our destination object?
Requirements:
- Your model is involved in a many-to-many relationship.
- Represent all models just once in memory (Identity Map).
- Your data structure should be easily navigable. Not only can you navigate from a parent to a child, but you can navigate from the child back up to the parent via which the child was originally fetched.
- I want to avoid introducing additional schema to the data model.
My best idea so far is to wrap each destination object with a context object (similar to how linked lists wrap nodes). The context object would maintain a pointer to the parent from which it was originally fetched. We would deal with each destination always through its wrapper. I believe this would be either the Proxy or Decorator pattern (I lean toward Proxy). (Wouldn't this essentially be the same idea as how the jQuery object encompasses many elements and multiple jQuery objects share references to the same elements?)
I considered maintaining a "current road trip" context variable and use that for navigating from a destination up to its containing road trip. This isn't as reliable as the actual "fetching context". In fact, it's a completely different tack and I'm not sure I like it.
I remember having the same issue with ActiveRecord (though it's been a while since I worked with it). In AR, if I started with RoadTrip1 and then fetched its destinations I couldn't very well navigate from a destination back up to the road trip (via some sort of fetching context). Instead, I'd have both parents (road trips) to consider and no indicator as to how I got there. Right?
Have others run into this problem before--that is, wanting to navigate backwards where backward navigation is confused by many parents? Have you ever asked "from which parent did I arrive here?" How did you answer that?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
您必须有 3 个类别:Roadtrip、Destination 和 Place。因此,您的目的地 A 和 B 是两个不同的对象,它们都引用同一个地点。
You have to have 3 classes: Roadtrip, Destination and Place. So your Destinations A and B are two different objects that both refer to the same Place.
我能够在使用代理模式后找到解决方案。我真正想要的是“获取上下文”的概念。我想知道我最初从哪个父母那里获取了模型(一个有多个父母的孩子)。
解决问题的关键是认识到维护获取上下文是我们的查询对象的责任,而不是我们的模型的责任。
我们从
roadtrip
模型开始并调用destinations
函数。该函数返回一个查询对象。此查询对象在设计上与 Rails 的 Arel 实现类似,因为它是惰性的,在调用all
、first
、each
等。查询对象有一个context
变量,该变量指向其父级 -roadtrip
模型。all
调用返回一个集合对象,其context
指向调用它的查询。集合中的每个项目都是一个代理(查询项目
),它包装每个底层目标
模型。代理维护对其集合的引用。实现这个代理的最简单的方法是这样的:这样你就可以将
上下文
分配给代理而不影响原来的。这允许“主”模型保持不变,从而进行身份映射。如果我们的模型维护对其集合的直接引用,这是不可能的,因为模型可以参与多个结果集(我们可以运行任意数量的查询),并且在我的场景中,我们期望只有一个上下文(父级)。
我们调用
activities
,它为我们提供了另一个查询对象,其上下文是代理的destination
。我们调用first
,我们得到的不是一个集合,而是一个代理activity
,它具有指向活动查询对象的上下文。因此,使用代理允许我们向上指向并“攀爬”对象层次结构,同时仍然维护身份映射模型,这些模型仍然不关心该层次结构,并且可以轻松参与多个集合(结果集)。
I was able to work up the solution I was after using the proxy pattern. What I really wanted was the concept of a "fetching context". I wanted to know from which parent I originally fetched a model (a child having multiple parents).
The key to solving the problem was realizing maintaining a fetching context was the responsibility of our query objects not our models.
We start with a
roadtrip
model and call thedestinations
function. This function returns a query object. This query object is similar in design to Rails' Arel implementation in that it's lazy, not actually returning any records until you callall
,first
,each
, etc. The query object has acontext
variable which points to its parent--theroadtrip
model.The
all
call returns a collection object whosecontext
points to the query from which it was invoked. Each item in the collection is a proxy (aqueried item
) that wraps each underlyingdestination
model. The proxy maintains a reference to its collection. The easiest way to achieve this proxy is like so:In this way you can assign the
context
to the proxy without affecting the original.This allows the "master" model to remain untouched and thus be identity mapped. This would not be possible if our model maintained direct reference to its collection since the model can take part in multiple result sets (we can run as many queries as we want) and, in my scenario, we expect only one context (parent).
We call
activities
which provides us with another query object whose context is the proxieddestination
. We callfirst
and rather than get a collection we get a proxiedactivity
having a context that points to the activities query object.As such, using proxies allows us point up and thus "climb" the object hierarchy while still maintaining identity-mapped models that remain oblivious to that hierarchy and can easily take part in multiple collections (result sets).