首页 > 解决方案 > 在构造函数中设置父子关系而不泄漏“this”变量

问题描述

我正在编写一些物理代码,将对象组织成层次关系,Body 类的每个对象都有一个父级(一个子类处理根条件)和零到多个子级。然而,为了实例化一个新的 Body,我必须将它添加到它的父级,因此构造函数将“this”泄漏给它的父级的 addChild 方法。

目前 Body 的 addChild 方法是私有的;在 Body 的构造函数之外访问它的唯一方法是通过 Body.setParent。但是,我担心在某些极端情况下,这仍然可能会暴露一个未完全实例化的对象。下面的代码被简化为仅相关的元素:

import java.util.Set;
import java.util.TreeSet;

public class Body {
    private Body parent;
    private Set<Body> children;

    public Body(Body parent) {
        // instantiates everything else first
        this.children = new TreeSet<Body>;
        this.parent = parent;

        parent.addChild(this);
    }

    public void setParent(Body parent) {
        this.parent.removeChild(this);
        this.parent = parent;
        parent.addChild(this);
    }

    private void addChild(Body child) {
        children.add(child);
    }

    private void removeChild(Body child) {
        children.remove(child);
    }
}

正如预期的那样,这段代码是“在构造函数中泄漏这个”,但似乎没有任何方法可以设置树结构。是否有另一种设计模式可用于避免 Body 在其构造函数完成运行之前可见的可能性?

标签: javaconstructorthread-safety

解决方案


保持构造函数简单是个好主意。在您的情况下,与另一个对象交互会很复杂。一个原因是错误处理在构造函数中可能很困难。

不要使用带有参数的构造函数,而是删除参数并像这样调用它。

var newBody = new Body();
newBody.setParent(parent);

构造函数看起来像这样。

public Body() {
    this.children = new TreeSet<Body>;
}

并更改 setParent 方法以检查是否没有当前父级。

public synchronized void setParent(Body newParent) {
    if (parent != null)
        parent.removeChild(this);
    parent = newParent;
    if (newParent != null)
        newParent.addChild(this);
}

请注意,我还将 setParent 参数的名称更改为不与类变量冲突。当您忘记使用“this”来区分您指的是哪个“父级”时,就会出现难以发现的错误。

无论如何,我删除的构造函数代码实际上只是 setParent 方法的重复。

我在方法中添加了同步,这样您就可以确保对该方法的多次调用是同步完成的,否则层次结构可能会损坏。

当然,有很多方法可以做到这一点。但这是我会使用的一种简单方法。


推荐阅读