模拟航空公司和机场
现在回到 trip-planner 应用程序,首先回顾一下域模型,并查看有没有潜在的 m:m 关系。在 第一篇文章 中,我创建了一个 Trip 类,如清单 1 所示:
清单 1. Trip 类
String name
String city
Date startDate
Date endDate
}
在 第二篇文章 中,我添加了一个 Airline 类(如清单 2 所示),以演示一个简单的 1:m 关系:
清单 2. Airline 类
static hasMany = [trip:Trip]
String name
String frequentFlier
}
这些类在当时具有其特定的用途 — 用于说明某一点的简单占位符 — 但是它们并不是一个严格的域模型。现在对最初的类进行改进,并添加一些更加健壮的内容。
我使用以前的方法创建了 Trip 类,因为当时看起来还不错。我当时说 “我正计划到芝加哥旅行” 或 “我计划在下个月 15 号到 20 号去纽约”。city、startDate 和 endDate 这样的字段似乎是 Trip 的自然属性。但是,现在看来,Trip 可能还会涉及到更多因素。
我住在科罗拉多州丹佛市 — 美国联合航空公司的中心城市。这意味着我通常可以直接飞到最终的目的地,但有时候需要中转多次。有时候一次旅行涉及到多个城市:“我正飞往波士顿,星期一到星期五我要在那里教课。当我在东海岸时,我需要在华盛顿附近参加星期六的一场会议。我将在星期天下午飞回来”。即使我幸运地找到了到一个特定城市的直达航班,而且我不会飞到其他城市,我的旅行涉及到的航班仍然不止一次 — 飞往目的地的航班和返回的航班。一个 Trip 可以包含多个 Flight。清单 3 定义 Trip 与 Flight 之间的关系:
清单 3. Trip 与 Flight 之间的 1:m 关系
static hasMany = [flights:Flight]
String name
}
class Flight{
static belongsTo = Trip
String flightNumber
Date departureDate
Date arrivalDate
}
请记住,使用 belongsTo 字段设置关系意味着,如果删除 Trip,也会删除所有相关的 Flight。如果我为空中交通管制员构建一个系统,我可能希望制定不同的架构决策。或者如果我尝试为同乘一个航班的多位乘客构建一个系统(一个 Flight 可以有多个 Passengers,一个 Passenger 也可以有多个 Flights),那么将一个航班绑定到一个特定的乘客可能是一个问题。但是我不会尝试为数百万乘客模拟全世界每天运行的数千次航班。在我的简单例子中,一个 Flight 的所有任务就是进一步描述一个 Trip。如果对于我来说,某个 Trip 不再重要,那么每个对应的 Flight 也是如此。
现在,我应该使用 Airline 类做什么呢?一个 Trip 可能涉及到多个不同的 Airline,而一个 Airline 又可以用于多个不同的 Trip。这两个类之间是一种明确的 m:m 关系,但是 Flight 似乎是添加 Airline 的恰当位置,如清单 4 所示。一个 Airline 可以有多个 Flight,而一个 Flight 只能有一个 Airline。
清单 4. 将 Airline 与 Flight 关联
static hasMany = [flights:Flight]
String name
String iata
String frequentFlier
}
class Flight{
static belongsTo = [trip:Trip, airline:Airline]
String flightNumber
Date departureDate
Date arrivalDate
}
您应该注意到两点。首先,Flight 中的 belongsTo 字段由一个值转变为多个值的散列映射(hashmap)。一个 Trip 可以有多个 Flight,一个 Airline 也可以有多个 Flight。
其次,我向 Airline 添加了一个新的 iata 字段。这个字段用于填写 International Air Transport Association (IATA) 编码。IATA 为每个航空公司分配了一个惟一的编码 — UAL 表示 United Airlines、COA 表示 Continental、DAL 表示 Delta,等等(参见 参考资料,获取 IATA 编码的完整列表)。
最后,您应该注意到我制定了另一个架构决策,这次加入了 Airline 和常飞顾客(frequent-flier)编号之间的关系。由于我假设只有一个用户使用这个系统,因此可以将 FrequentFlier 作为 Airline 类的一个属性。每个航空公司最多只有一个 frequent-flier 编号,所以这是最简单的可能的解决方案。如果这次旅行计划的需求发生了更改,而且我需要支持多个用户,那么就出现了另一个 m:m 关系。一个乘客可以有多个 frequent-flier 编号,一个航空公司也可以有多个 frequent-flier 编号。创建一个连接表来管理这个关系会非常适合。现在我将使用简单的解决方案,但是如果需求发生了更改,我会将 FrequentFlier 字段标记为未来的一个重构点。