我是一些家谱软件(用C++和Qt编写)的开发人员。直到我的一位客户给我邮寄了一份错误报告,我才发现问题。问题是,客户有两个孩子和自己的女儿,因此,由于错误,他无法使用我的软件。
这些错误是我对正在处理的族图的各种断言和不变量的结果(例如,在走完一个循环后,程序声明X不能既是Y的父亲又是Y的祖父)。
如何在不删除所有数据断言的情况下解决这些错误?
我是一些家谱软件(用C++和Qt编写)的开发人员。直到我的一位客户给我邮寄了一份错误报告,我才发现问题。问题是,客户有两个孩子和自己的女儿,因此,由于错误,他无法使用我的软件。
这些错误是我对正在处理的族图的各种断言和不变量的结果(例如,在走完一个循环后,程序声明X不能既是Y的父亲又是Y的祖父)。
如何在不删除所有数据断言的情况下解决这些错误?
当前回答
放松你的断言。
而不是改变规则,这可能对99.9%的客户在输入数据时发现错误非常有帮助。
相反,将其从错误“无法添加关系”更改为警告“无论如何添加”。
其他回答
这是家谱的问题:它们不是树。它们是有向无环图或DAG。如果我正确理解人类生殖生物学的原理,就不会有任何周期。
据我所知,即使是基督徒也接受表兄弟之间的婚姻(以及孩子),这将把家谱变成家庭DAG。
这个故事的寓意是:选择正确的数据结构。
放松你的断言。
而不是改变规则,这可能对99.9%的客户在输入数据时发现错误非常有帮助。
相反,将其从错误“无法添加关系”更改为警告“无论如何添加”。
断言不能生存于现实
通常,断言在与真实世界数据的接触中无法生存。这是软件工程过程中的一部分,决定您要处理哪些数据,哪些数据超出了范围。
循环族图
关于家族“树”(事实上,它是完整的图表,包括循环),有一个很好的轶事:
我娶了一个寡妇,她有一个成年的女儿。我的父亲经常来看我们,他爱上了我的继女并娶了她。结果,我的父亲成了我的儿子,我的女儿成了我母亲。一段时间后,我给了我妻子一个儿子,他是我父亲和我叔叔的兄弟。我父亲的妻子(也是我的女儿和母亲)有一个儿子。结果,我得到了同一个人的一个哥哥和一个孙子。我的妻子现在是我的祖母,因为她是我母亲的母亲。所以我是我妻子的丈夫,同时也是我妻子的继孙。换句话说,我是我自己的爷爷。
当你考虑到代孕或“模糊的父亲身份”时,事情变得更加奇怪。
如何应对
将周期定义为超出范围
你可以决定你的软件不应该处理这种罕见的情况。如果出现这种情况,用户应使用不同的产品。这使得处理更常见的情况更加健壮,因为您可以保留更多断言和更简单的数据模型。
在这种情况下,为您的软件添加一些良好的导入和导出功能,以便用户可以在必要时轻松迁移到其他产品。
允许手动关系
您可以允许用户添加手动关系。这些关系不是“一流公民”,即软件按原样对待它们,不检查它们,也不在主数据模型中处理它们。
然后,用户可以手动处理罕见的情况。您的数据模型仍然非常简单,您的断言将继续存在。
小心手动关系。有一种诱惑是让它们完全可配置,从而创建一个完全可配置的数据模型。这是行不通的:你的软件无法扩展,你会发现奇怪的bug,最终用户界面将变得不可用。这种反模式被称为“软编码”,《每日WTF》中有很多这样的例子。
使数据模型更灵活,跳过断言,测试不变量
最后的办法是使数据模型更灵活。您必须跳过几乎所有的断言,并将数据模型建立在一个完整的图表上。正如上面的例子所示,你很容易成为自己的祖父,因此你甚至可以拥有自行车。
在这种情况下,您应该广泛测试您的软件。您必须跳过几乎所有断言,因此很有可能会出现其他错误。
使用测试数据生成器检查异常测试用例。有Haskell、Erlang或C的快速检查库。对于Java/Scala,有ScalaCheck和Nyaya。一个测试思路是模拟一个随机种群,让它随机杂交,然后让你的软件先导入然后导出结果。期望是,输出中的所有连接也在输入中,反之亦然。
属性保持不变的情况称为不变量。在这种情况下,不变量是模拟人口中个体之间的一组“浪漫关系”。尝试找到尽可能多的不变量,并用随机生成的数据测试它们。不变量可以是函数,例如:
即使你增加了更多的“浪漫关系”,叔叔仍然是叔叔每个孩子都有父母有两代人的人口至少有一个祖父母
也可以是技术性的:
您的软件不会在多达100亿成员的图形上崩溃(无论有多少互连)软件的缩放比例为O(节点数)和O(边数^2)您的软件可以保存和重新加载多达100亿成员的每个家庭图
通过运行模拟测试,您将发现许多奇怪的角落案例。修复它们需要很多时间。此外,您将失去很多优化,您的软件将运行得慢得多。你必须决定它是否值得,这是否在你的软件范围内。
一些答案显示了保持断言/不变量的方法,但这似乎是滥用断言/不变量。断言是为了确保应该是真的东西是真的,不变量是为了确保不应该改变的东西不会改变。
你在这里断言的是乱伦关系是不存在的。显然它们确实存在,因此您的断言是无效的。您可以解决这个断言,但真正的错误在于断言本身。应删除断言。
复制父项(或使用符号链接/引用)。
例如,如果您使用的是分层数据库:
$ #each person node has two nodes representing its parents.
$ mkdir Family
$ mkdir Family/Son
$ mkdir Family/Son/Daughter
$ mkdir Family/Son/Father
$ mkdir Family/Son/Daughter/Father
$ ln -s Family/Son/Daughter/Father Family/Son/Father
$ mkdir Family/Son/Daughter/Wife
$ tree Family
Family
└── Son
├── Daughter
│ ├── Father
│ └── Wife
└── Father -> Family/Son/Daughter/Father
4 directories, 1 file