应用“使无效状态不可代表”

2020-10-07 17:31:01

用开始日期和结束日期((date,date))表示一段时间有一个简单的方法:

如果我们需要表示拆分成连续时段的时间线,则可能很容易将其表示为一系列时段(例如List(Date,Date)):

我们可以改进这种表示,以便连续和非重叠的约束始终有效,并且我们可以用一种可能会提醒您数据库规范化的方式来实现-通过删除冗余。

在结构良好的连续时间线中,相邻时段的联合开始/结束是多余的。连续的、不重叠的拆分可以简单地由一组日期(设置日期)表示:

当您考虑如何进一步拆分时间线时,您可以开始看到这种表示如何简化了系统。在列表表示中,拆分句点需要仔细修改数据结构并确保不违反约束。在“日期集”表示中,您只需向该集添加一个日期即可。

有时,将周期表示为开始和结束日期序列仍然很有用。将日期集投影到此表单中是很简单的。只要规范表示是集合,约束仍然成立。

在这个系统中,客户根据合同向我们支付经常性租金,合同持续一段固定的时间,当合同到期时,我们回到“默认合同”。客户可以有多份固定合同,也可以随时签订新合同。

更糟糕的是,这些合同的API允许您修改每个单独的合同,无论是固定的还是默认的,而不需要防范这些状态。这说明了糟糕的表示选择是如何通过系统设计来传播自己的。

这种糟糕的选择不仅仅是一个理论问题--合同中的漏洞不止一次被发现,需要数小时的工程努力才能找到并修复。

通过从合同表中删除“默认”合同,很容易改进这一点。如果客户没有固定合同,则假定他们签订的是默认合同:

现在不能再有任何差距,合同的结束日期不再需要是可选的,因为它只代表固定的合同。

值得重申的是,如果前一种形式更方便的话,可以使用数据库视图将该表示投影到以前的表示中。重要的是底层表示会强制执行这些约束,您如何查看数据并不重要。

与第一种情况一样,更好的表示使数据结构的操作变得更简单。在这种情况下,添加新的固定合同大大简化。不需要创建或修改默认合同,也不需要确保合同是连续的。

如果这一改进对您来说很明显,您可能想知道最初的设计是如何发生的。

在这种思维模式下,固定合同是对象,默认合同是对象,这些概念中的每一个都必须被具体化为表中的一行,并且不能推断。除了存储或检索对象之外,不信任使用数据库提供的任何功能。

这种方法与质量关系设计和使无效状态不可表示的原则背道而驰。

在某种程度上,它可能感觉“更简单”,因为你并不真的需要考虑你的设计。但是,正如我们在这里看到的,这种缺乏预见性的做法不可避免地会导致复杂性。