我不熟悉 GORM/Grails。研究表明它本质上是 Java。
这是我在纯 Java 环境中使用的那种结构。
一个Person
包含所有关系和一些简单实用方法的对象(例如getSiblings
遍历所有父母的孩子的你)。
这些关系将被管理为Set
s 以减少您必须执行的引用完整性检查的数量(但它不会消除它)。
将使用工厂方法创建和销毁关系。我enum
在我的示例中使用了一个。
为简洁起见,我没有实现 getter 和 setter。您的最终解决方案应该正确使用它们。
我没有尝试任何你可能需要的更微妙的机制——比如将一个孩子添加到一对特定的父母中。在这种情况下,这将是可以实现的,但不必要地复杂。
public class Person {
// Using HashSet to limit possibilities of cycles - we can assume no person exists twice in each.
private final Set<Person> parents = new HashSet<>();
private final Set<Person> partners = new HashSet<>();
private final Set<Person> children = new HashSet<>();
// Person details.
private final String name;
// Constructor.
public Person(String name) {
this.name = name;
}
// Extract all siblings as all children of all parents.
public Set<Person> getSiblings() {
Set<Person> siblings = new HashSet<>();
for (Person parent : parents) {
siblings.addAll(parent.children);
}
return siblings;
}
}
// A factory to handle family connections.
public enum Connection {
ParentOf {
@Override
void connect(Person a, Person b) {
// Connect through a's children and b's parents.
connect(a.children, b, b.parents, a);
}
@Override
void disconnect(Person a, Person b) {
// Connect through a's children and b's parents.
disconnect(a.children, b, b.parents, a);
}
},
PartnerOf {
@Override
void connect(Person a, Person b) {
// Connect through a's children and b's parents.
connect(a.partners, b, b.partners, a);
}
@Override
void disconnect(Person a, Person b) {
// Connect through a's children and b's parents.
disconnect(a.partners, b, b.partners, a);
}
},
ChildOf {
@Override
void connect(Person a, Person b) {
// The opposit of parent.
ParentOf.connect(b, a);
}
@Override
void disconnect(Person a, Person b) {
// The opposit of parent.
ParentOf.disconnect(b, a);
}
};
abstract void disconnect(Person a, Person b);
abstract void connect(Person a, Person b);
/**
* Connect b to a through aSet and a to b through bSet
*
* @param aSet The set in person a
* @param b The b person
* @param bSet The set in person b
* @param a The a person
*/
void connect(Set<Person> aSet, Person b, Set<Person> bSet, Person a) {
aSet.add(b);
bSet.add(a);
}
/**
* Reverse of connect.
*
* @param aSet The set in person a
* @param b The b person
* @param bSet The set in person b
* @param a The a person
*/
void disconnect(Set<Person> aSet, Person b, Set<Person> bSet, Person a) {
aSet.remove(b);
bSet.remove(a);
}
}
public class Tree {
// Safe version - ensuring relationships are maintained.
public void connect(Person a, Connection c, Person b) {
c.connect(a, b);
}
public void test() {
Person adam = new Person("Adam");
Person eve = new Person("Eve");
Person cain = new Person("Cain");
Person abel = new Person("Abel");
connect(adam, Connection.PartnerOf, eve);
connect(adam, Connection.ParentOf, cain);
connect(eve, Connection.ParentOf, cain);
connect(adam, Connection.ParentOf, abel);
connect(eve, Connection.ParentOf, abel);
}
}
你问题的最后一部分:
- 如何保证生成的图没有环?
实际上 - 没有一些巨大的努力 - 你不能!我怀疑这是不可能的,但也要记住,真正的家谱中可能存在循环。用户意外创建循环是很常见的。以不止一种方式从同一个祖先继承下来也很常见——只要表亲结婚,这些都是完全合法的。
这不应该是你的结构的要求,它应该是你的结构的一个可发现的特征。