Java 在构造函数中使用 "this" 的双向对象引用

Java Bidirectional Object References using "this" in Constructor

我正在尝试创建对象和组件对象,它们彼此具有双向引用。在这个例子中,我有一辆自行车 class 和一辆轮子 class。我考虑的一个选项(选项 1)是让 Bike 创建 Wheel,然后在其构造函数中将其自身的引用传递给 Wheel。但是,我读到我不应该在构造函数之外传递 "this",最好在 Bike 构造函数之外创建 Wheel 对象。所以我应该先创建 Wheel,然后将其传递给 Bike,然后为 Wheel 调用 setBike() 方法(选项 2)。在我看来,选项 1 是在 Bike 和 Wheel 之间创建双向引用的最简单方法,但它似乎也违反了一些设计原则。有人可以解释为什么选项 2 优于选项 1 吗?

选项 1:

public class Wheel {

    private Bike bike;

    Wheel(Bike bike) {
        this.bike = bike;
    }
}

public class Bike {

    private Wheel wheel;

    Bike() {
        this.wheel = new Wheel(this);
    }
}

Bike bike = new Bike();

选项 2:

public class Wheel {

    private Bike bike;

    public void setBike(Bike bike) {
        this.bike = bike;
    }
}

public class Bike {

    private Wheel wheel;

    Bike(Wheel wheel) {
        this.wheel = wheel;
    }
}

Wheel wheel = new Wheel();
Bike bike = new Bike(wheel);
wheel.setBike(bike);

第一个选项并没有你想的那么理想。它不仅通过 this 而对象未完全初始化:this.wheel = new Wheel(this); 在这里它不应该造成任何问题,因为这是一个非常简化的用法,但这个选项也会产生一个设计问题:wheel 依赖是在构造函数中硬编码。您不能 switch/mock 依赖实例。

一般来说,双向关系会产生一些约束,迫使您进行一些设计权衡:两次设置值,不可变性,至少暴露 setter,等等……注意您可以通过引入一些额外的抽象来解决其中的一些问题。

但是为了简单起见,至少创建两个对象之一 "alone" 是正确的选择,如您的第二个选项所示。如果您在构造函数中处理相互关系,则可以节省 setter 部分调用:

Wheel wheel = new Wheel();
Bike bike = new Bike(wheel);
// wheel.setBike(bike); // not required now

自行车定义为:

Bike(Wheel wheel){
  this.wheel = wheel;
  wheel.setBike(this);
}

更进一步,如上所述,您可以通过引入一些抽象来减少两者之间的耦合 类 以及关联两个实例的方式。
例如,您可以引入自行车和车轮接口,这是客户操纵自行车和车轮的唯一方式。通过这种方式,您可以执行 bi-relationship 甚至在内部实现一定程度的不变性,客户永远不会看到(感谢 private 嵌套 类).
当你开发一个 API 时,这通常是一种有助于确保某些不变量和规则的方法。

想法是客户只做:

Bike bike = CarFactory.of("super bike", "hard wheel");
Wheel wheel = bike.getWheel();

接口:

public interface Wheel {
    String getBarProp();
    Bike getBike();
}

public interface Bike {
    String getFooProp();
    Wheel getWheel();
}

和客户端 API 而不是 CarFactory 中的 public 实现:

public class CarFactory {

    private static class BikeDefaultImpl implements Bike {

        private final String fooProp;
        private Wheel wheel;

        public BikeDefaultImpl(String fooProp) {
            this.fooProp = fooProp;
        }

        @Override
        public String getFooProp() {
            return fooProp;
        }

        @Override
        public Wheel getWheel() {
            return wheel;
        }
    }


    private static class WheelDefaultImpl implements Wheel {

        private final String barProp;
        private Bike bike;

        public WheelDefaultImpl(String barProp) {
            this.barProp = barProp;
        }

        @Override
        public String getBarProp() {
            return barProp;
        }

        @Override
        public Bike getBike() {
            return bike;
        }
    }


    public static Bike of(String bikeProp, String wheelProp) {
        BikeDefaultImpl bike = new BikeDefaultImpl(bikeProp);
        WheelDefaultImpl wheel = new WheelDefaultImpl(wheelProp);
        bike.wheel = wheel;
        wheel.bike = bike;
        return bike;
    }

}

您还会注意到,以这种方式不再需要在构造函数中使用 this,因为我们可以在内部自由访问 Bike 和 Wheel 实现的字段。