您正在编写Java程序。您可以用互相引用的对象对您的域实体进行建模。您希望您的对象是不变的,因此在课堂中使用final
成员。
现在您检测到一个问题:您的对象图包含周期。您如何实例化对象?
要说明,让我们假设您有一个Parent
和class Child
,并且每个都使用最终成员引用彼此:
class Parent {
final Child child;
final String name;
Parent(Child child, String name) {
this.child = child
this.name = name;
}
public String toString() {
return name + " is the parent of " + this.child.name;
}
}
static class Child {
final Parent parent;
final String name;
Child(Parent parent, String name) {
this.parent = parent;
this.name = name;
}
public String toString() {
return name + " is the child of " + this.parent.name;
}
}
当您尝试实例化父母/子对时,您将无法做到这一点。
问题在于,您需要孩子的实例来实例化父母和父母实例来实例化孩子,并且由于课堂内的这些参考是最终的,因此您不能在拥有另一个之前就拥有一个。您必须以某种方式同时实例化...或某种情况。
但是,可以做到这一点,这就是如何(省略所有钟声和哨子,要使它成为线程安全或其他好的哨子,只是提出主要想法):
- 使用构建器模式创建您的对象
- 在构建器中,请参考您在呼叫上返回
build()
的实例,并允许通过公共方法设置此实例,例如setProduct(T instance)
。 - 将X类的构建器传递到x 的构造函数中
- 在构造函数的第一行中,将构建器的实例设置为
this
,即。builder.setProduct(this)
。 - 而不是将类实例传递给构造函数,而是通过返回
build()
的实例的构建器。
这是代码:
public class ImmutableCyclicObjectGraphExperiment {
static class Parent {
final Child child;
final String name;
Parent(ParentBuilder builder, ChildBuilder childBuilder, String name) {
builder.setInstance(this);
this.child = childBuilder.build();
this.name = name;
}
public String toString() {
return name + " is the parent of " + this.child.name;
}
}
static class Child {
final Parent parent;
final String name;
Child(ChildBuilder builder, ParentBuilder parentBuilder, String name) {
builder.setInstance(this);
this.parent = parentBuilder.build();
this.name = name;
}
public String toString() {
return name + " is the child of " + this.parent.name;
}
}
static class ParentBuilder {
ChildBuilder childBuilder;
String name;
Parent instance = null;
public ParentBuilder() {
}
void setInstance(Parent instance){
this.instance = instance;
}
Parent build() {
if (this.instance == null) {
this.instance = new Parent(this, this.childBuilder, this.name);
}
return this.instance;
}
public ParentBuilder child(ChildBuilder childBuilder) {
this.childBuilder = childBuilder;
return this;
}
public ParentBuilder name(String name) {
this.name = name;
return this;
}
}
static class ChildBuilder {
ParentBuilder parentBuilder;
String name;
Child instance = null;
Child build() {
if (this.instance == null) {
this.instance = new Child(this, parentBuilder, name);
}
return this.instance;
}
void setInstance(Child instance) {
this.instance = instance;
}
public ChildBuilder parent(ParentBuilder parentBuilder) {
this.parentBuilder = parentBuilder;
return this;
}
public ChildBuilder name(String name) {
this.name = name;
return this;
}
}
public static void main(String[] args) {
ParentBuilder pb = new ParentBuilder();
ChildBuilder cb = new ChildBuilder();
pb
.name("Anakin")
.child(cb);
cb
.name("Luke")
.parent(pb);
Parent p = pb.build();
Child c = cb.build();
System.out.println(p);
System.out.println(c);
}
}
发生的事情是,当ParentBuilder.build()
首先在main()
函数中调用时,构建器将Parent
的构造函数和引用this
称为所创建的对象的构造函数立即将其走私到构建器中。然后,在同一构造函数的下一行中,childBuilder
作为构造函数参数传递,用于获得Child
实例。 childBuilder
称为Child
的构造函数,传递了一个ParentBuilder
实例,这与我们在main()
函数中已经使用的相同来开始实例化Parent
,并且已经在呼叫堆栈上仍将对Parent
的走私引用保持了几个级别。因此,在Child
构造函数中,parentBuilder.build()
返回走私参考,即使其引用的对象尚未完全实例化,也可以分配给最终成员。当Child c = cb.build()
在main()
函数中调用时,该实例已经完全实例化并返回而无需调用构造函数。
只要发生在同一线程中,您保证不对这些实例做任何事情,而是引用它们,您应该安全。
正如预期的,调用main()
方法打印以下内容:
Anakin is the parent of Luke
Luke is the child of Anakin
不变和循环,所有必要的间接是在实例化时完成的。那不是很好吗?